如何正确清理MSMQ监听器?

时间:2012-04-17 09:34:12

标签: .net msmq

我有使用异步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时),除非未使用缓存您拥有对队列的独占访问权限。

2 个答案:

答案 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秒)。