MessageQueue.BeginReceive如何工作以及如何正确使用它?

时间:2011-09-15 10:00:12

标签: c# asynchronous msmq message-queue

我目前有一个后台主题。在这个线程中是一个无限循环。

此循环偶尔会更新数据库中的某些值,然后在MessageQueue上侦听1秒(使用queue.Receive(TimeSpan.FromSeconds(1)))。

只要没有消息进入,此调用就会在内部抛出MessageQueueException(Timeout),然后循环继续。如果有消息,则呼叫通常会返回并处理消息,然后循环继续。

这会导致第一次机会异常的批次(每秒,除了有一条要处理的消息),这会使调试输出发生错误,并且当我忘记排除MessageQueueExceptions时也会在调试器中中断。

那么MessageQueue的异步处理是如何正确完成的,同时仍然确保只要我的应用程序运行,就会监视队列,并且数据库也会偶尔更新一次。当然这里的线程不应该耗尽100%的CPU。

我只需要大图或暗示一些正确完成的异步处理。

3 个答案:

答案 0 :(得分:4)

我建议为MessageQueue的ReceiveCompleted事件注册一个委托,而不是在一个线程中循环,如下所述:

using System; using System.Messaging;

namespace MyProject { /// /// Provides a container class for the example. /// public class MyNewQueue {

    //**************************************************
    // Provides an entry point into the application.
    //       
    // This example performs asynchronous receive operation
    // processing.
    //**************************************************

    public static void Main()
    {
        // Create an instance of MessageQueue. Set its formatter.
        MessageQueue myQueue = new MessageQueue(".\\myQueue");
        myQueue.Formatter = new XmlMessageFormatter(new Type[]
            {typeof(String)});

        // Add an event handler for the ReceiveCompleted event.
        myQueue.ReceiveCompleted += new 
            ReceiveCompletedEventHandler(MyReceiveCompleted);

        // Begin the asynchronous receive operation.
        myQueue.BeginReceive();

        // Do other work on the current thread.

        return;
    }


    //**************************************************
    // Provides an event handler for the ReceiveCompleted
    // event.
    //**************************************************

    private static void MyReceiveCompleted(Object source, 
        ReceiveCompletedEventArgs asyncResult)
    {
        // Connect to the queue.
        MessageQueue mq = (MessageQueue)source;

        // End the asynchronous Receive operation.
        Message m = mq.EndReceive(asyncResult.AsyncResult);

        // Display message information on the screen.
        Console.WriteLine("Message: " + (string)m.Body);

        // Restart the asynchronous Receive operation.
        mq.BeginReceive();

        return; 
    }
}

}

//************************************************** // Provides an entry point into the application. // // This example performs asynchronous receive operation // processing. //************************************************** public static void Main() { // Create an instance of MessageQueue. Set its formatter. MessageQueue myQueue = new MessageQueue(".\\myQueue"); myQueue.Formatter = new XmlMessageFormatter(new Type[] {typeof(String)}); // Add an event handler for the ReceiveCompleted event. myQueue.ReceiveCompleted += new ReceiveCompletedEventHandler(MyReceiveCompleted); // Begin the asynchronous receive operation. myQueue.BeginReceive(); // Do other work on the current thread. return; } //************************************************** // Provides an event handler for the ReceiveCompleted // event. //************************************************** private static void MyReceiveCompleted(Object source, ReceiveCompletedEventArgs asyncResult) { // Connect to the queue. MessageQueue mq = (MessageQueue)source; // End the asynchronous Receive operation. Message m = mq.EndReceive(asyncResult.AsyncResult); // Display message information on the screen. Console.WriteLine("Message: " + (string)m.Body); // Restart the asynchronous Receive operation. mq.BeginReceive(); return; } }

来源:https://docs.microsoft.com/en-us/dotnet/api/system.messaging.messagequeue.receivecompleted?view=netframework-4.7.2

答案 1 :(得分:3)

与Jamie Dixon的评论相反,情景非同寻常。请注意方法及其参数的命名:BeginReceive(TimeSpan timeout)

如果该方法名为BeginTryReceive,那么如果没有收到消息则完全正常。将其命名为BeginReceive(或同步版本的Receive)意味着消息应该进入队列。 TimeSpan参数名为timeout也很重要,因为超时是例外。超时意味着预期响应,但没有给出响应,并且调用者选择停止等待并假定发生了错误。当你以1秒的超时时间调用BeginReceive / Receive时,你会说如果到那时没有消息进入队列,那么一定有问题,我们需要处理它。

我实现这个的方式,如果我理解你想要做的正确,是这样的:

  • 如果我没有看到空队列作为错误,请使用非常大的超时调用BeginReceive,或者甚至没有超时。
  • 将事件处理程序附加到ReceiveCompleted事件,1)处理消息,2)再次调用BeginReceive。
  • 我不会使用无限循环。当使用BeginReceive等异步方法时,这既是不好的做法,也是完全冗余的。
  • 编辑:要放弃任何客户端未读取的队列,请让队列编写者查看队列以确定它是否“死”。

编辑:我有另一个建议。由于我不知道您的申请的细节,我不知道它是否可行或适当。在我看来,你基本上建立了客户端和服务器之间的连接,消息队列作为通信通道。为什么这是一个“连接”?因为如果没有人正在侦听,则不会写入队列。我认为,这几乎就是一种联系。使用套接字或命名管道传输消息不是更合适吗?这样,客户端只需在完成读取后关闭Stream对象,就会立即通知服务器。正如我所说的,我不知道它是否适用于你正在做的事情,但感觉它是一个更合适的沟通渠道。

答案 2 :(得分:3)

您是否考虑过从MessageEnumerator返回的MessageQueue.GetMessageEnumerator2

  • 您将获得队列的动态内容,以便在迭代期间检查和删除队列中的消息。
  • 如果没有消息,那么MoveNext()将返回false并且您不需要捕获第一次机会异常
  • 如果在开始迭代后有新消息,那么它们将被迭代(如果它们放在游标之后)。
  • 如果在游标之前有新消息,则可以重置迭代器或继续(如果此时不需要优先级较低的消息)。