我有使用异步API支持从MSMQ读取的代码,即使用BeginReceive(),EndReceive()和ReceivedCompleted事件。基本模式是(取自MessageQueue.ReceiveCompleted Event ...
void StartListening()
{
_msgQ.ReceiveCompleted += ReceiveCompletedEventHandler(FooReceiveCompleted);
_msgQ.BeginReceive();
}
void FooReceiveCompleted(Object source, ReceiveCompletedEventArgs asyncResult)
{
Message msg = _msgQ.EndReceive();
// Do stuff with message.
// Set up listening for next message.
_msgQ.BeginReceive();
}
void StopListening()
{
_msgQ.Close();
}
我可以看到的问题是,总有一个等待新消息的待处理BeginReceive(),并且通过阅读.Net文档,似乎没有官方/推荐的方法来清除它以便停止听。
如果我在没有收到消息的情况下调用EndReceive(),则呼叫将阻塞,直到消息可用。或者,似乎Close()不会清除MSMQ上的底层句柄(因此也不会清除挂起的侦听器),除非将EnableConnectionCache设置为false,否则句柄将被缓存,并且在关闭调用时不会清除。我可以做到这一点但理想情况下我想使用缓存。
我能看到的唯一其他选项是启用缓存,然后调用静态方法MessageQueue.ClearConnectionCache(),这可能是应用程序域范围的,因此会影响与我尝试关闭的队列无关的队列。
附录: 其他选项(来自MessageQueue.Close())...
Close并不总是释放队列的读写句柄, 因为他们可能会被分享您可以执行以下任何步骤 确保Close释放读取和写入句柄到队列:
使用独占访问权限创建MessageQueue。为此,请致电 MessageQueue(String,Boolean)或MessageQueue(String,Boolean, Boolean)构造函数,并将sharedModeDenyReceive参数设置为 真。
创建禁用连接缓存的MessageQueue。为此, 调用MessageQueue(String,Boolean,Boolean)构造函数并设置 enableConnectionCache参数为false。
禁用连接缓存。为此,请设置EnableConnectionCache 财产到假。
因此,我对文档API的第一印象是您无法正确终止队列(使用BeginReceive / EndReceive时),除非未使用缓存或您拥有对队列的独占访问权限。
答案 0 :(得分:0)
请看Event Driven consumer。 截至2018-12-19此网址返回404。
答案 1 :(得分:0)
问题的关键在于MSDN上MessageQueue.ReceiveCompleted Event的示例C#使用没有参数的BeginReceive()。这是一个立即返回给调用者的异步调用,但是它会导致一个可能具有很长生命周期的未完成的异步操作。
当我们尝试在MessageQueue上调用Close()时,这种未完成的异步操作会阻止正确释放MessageQueue。
一种解决方案是使用BeginReceive(超时);即使没有消息,这也会导致ReceiveCompleted事件被触发,此时我们可以测试一个标志,看是否正在请求关闭并允许清理正常进行。也就是说,关闭消息队列的外部请求必须等待,例如,一个WaitHandle,ReceiveCompleted事件将发出信号。因此,该模式最好使用几秒钟的短BeginReceive()超时(理想情况下为1或2秒)。