3

我有使用异步 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(),则调用会阻塞,直到有消息可用。或者,除非 EnableConnectionCache 设置为 false,否则 Close() 似乎不会清理 MSMQ 上的底层句柄(因此也不会清理挂起的侦听器),否则句柄会被缓存并且不会在调用关闭时被清理。我可以这样做,但理想情况下我想使用缓存。

我能看到的唯一其他选项是启用缓存,然后调用静态方法 MessageQueue.ClearConnectionCache() 可能是应用程序域范围,因此会影响与我尝试关闭的队列无关的队列。

附录:附加选项(来自MessageQueue.Close())...

关闭并不总是释放队列的读写句柄,因为它们可能是共享的。您可以采取以下任何步骤来确保 Close 将读取和写入句柄释放到队列:

创建具有独占访问权限的 MessageQueue。为此,请调用 MessageQueue(String, Boolean) 或 MessageQueue(String, Boolean, Boolean) 构造函数,并将 sharedModeDenyReceive 参数设置为 true。

创建禁用连接缓存的 MessageQueue。为此,请调用 MessageQueue(String, Boolean, Boolean) 构造函数并将 enableConnectionCache 参数设置为 false。

禁用连接缓存。为此,请将 EnableConnectionCache 属性设置为 false。

因此,我对文档 API 的第一印象是,除非不使用缓存或您对队列具有独占访问权限,否则您无法正确终止队列(使用 BeginReceive/EndReceive 时) 。

4

2 回答 2

0

看看这里事件驱动的消费者截至 2018 年 12 月 19 日,此 URL 返回 404。

于 2012-04-18T16:38:44.977 回答
0

问题的症结在于MSDN 上MessageQueue.ReceiveCompleted Event的示例 C#使用了没有参数的 BeginReceive()。这是一个立即返回给调用者的异步调用,但是它会导致一个未完成的异步操作,该操作可能具有很长的生命周期。

当我们尝试在 MessageQueue 上调用 Close() 时,这个未完成的异步操作会阻止 MessageQueue 的正确释放。

一种解决方案是使用 BeginReceive(Timeout); 即使没有消息,这也会导致 ReceiveCompleted 事件触发,此时我们可以测试一个标志以查看是否正在请求关闭并允许清理正常进行。也就是说,关闭消息队列的外部请求必须等待,例如,WaitHandle,ReceiveCompleted 事件将发出信号。因此,该模式在几秒(理想情况下为 1 或 2 秒)的短暂 BeginReceive() 超时下效果最佳。

于 2015-08-19T10:58:33.720 回答