MessageQueue和Async / Await

时间:2013-04-19 08:51:27

标签: c# message-queue async-await

我只想以异步方式接收我的消息!并冻结我的UI

    public async void ProcessMessages()
    {
        MessageQueue MyMessageQueue = new MessageQueue(@".\private$\MyTransactionalQueue");
        MyMessageQueue.Formatter = new XmlMessageFormatter(new Type[] { typeof(string) });

        while (true)
        {
            MessageQueueTransaction MessageQueueTransaction = new MessageQueueTransaction();
            MessageQueueTransaction.Begin();

            ContainError = false;
            ProcessPanel.SetWaiting();

            string Body = MyMessageQueue.Receive(MessageQueueTransaction).Body.ToString();

            //Do some process with body string.

            MessageQueueTransaction.Commit();
        }
    }

我只是像任何常规方法一样调用方法,并且它的工作正常! 当我使用BackgroundWorkers而不是async / await

时,此代码曾经工作

想法?

2 个答案:

答案 0 :(得分:25)

正如Stephen所写,async不会在一个线程中运行你的代码。幸运的是,您可以使用TaskFactory.FromAsyncMessageQueue.BeginReceive / MessageQueue.EndReceive来异步接收消息:

    private  async Task<Message> MyAsyncReceive()
    {
        MessageQueue queue=new MessageQueue();
        ...
        var message=await Task.Factory.FromAsync<Message>(
                           queue.BeginReceive(),
                           queue.EndReceive);

        return message;

    }

您应该注意,虽然没有使用Transaction的BeginReceive版本。来自BeginReceive的文档:

  

不要将异步调用BeginReceive与事务一起使用。如果要执行事务异步操作,请调用BeginPeek,并将事务和(同步)Receive方法放在为peek操作创建的事件处理程序中。

这是有道理的,因为无法保证您需要等待多长时间的响应或哪个线程最终将处理完成的调用。

要使用交易,你可以这样写:

    private  async Task<Message> MyAsyncReceive()
    {
        var queue=new MessageQueue();

        var message=await Task.Factory.FromAsync<Message>(queue.BeginPeek(),queue.EndPeek);

        using (var tx = new MessageQueueTransaction())
        {
            tx.Begin();

            //Someone may have taken the last message, don't wait forever
            //Use a smaller timeout if the queue is local
            message=queue.Receive(TimeSpan.FromSeconds(1), tx);
            //Process the results inside a transaction
            tx.Commit();
        }
        return message;
    }

<强>更新

正如Rob指出的那样,原始代码使用了message返回的PeekPeek可能在ReceiveReceive之间发生了变化。在这种情况下,第二条消息将丢失。

如果另一个客户端读取队列中的最后一条消息,仍然有可能阻塞。为了防止这种情况发生,{{1}}应该有一个很小的超时。

答案 1 :(得分:6)

async does not run your code on a background thread.上面的代码应该导致编译器警告,告诉您方法将同步运行。

如果要在后台线程上执行方法,请使用TaskEx.Run

public void ProcessMessages()
{
  ...
}

TaskEx.Run(() => ProcessMessages());