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

问题描述 投票:4回答:3

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

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

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

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

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

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

c# asynchronous msmq message-queue
3个回答
3
投票

你有没有考虑过从MessageEnumerator归来的MessageQueue.GetMessageEnumerator2

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

4
投票

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

使用系统;使用System.Messaging;

namespace MyProject {/// ///为示例提供容器类。 ///公共类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; 
    }
}

}

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


3
投票

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

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

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

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

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

© www.soinside.com 2019 - 2024. All rights reserved.