11

我只想以异步方法接收我的消息!它冻结了我的用户界面

    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 时,此代码可以正常工作

想法?

4

2 回答 2

27

正如斯蒂芬所写,异步不会在线程中运行您的代码。幸运的是,您可以使用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;

    }

您应该注意,虽然没有使用事务的 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 指出的那样,原始代码使用了messagefrom 的返回值Peek,它可能在Peek和之间发生了变化Receive。在这种情况下,第二条消息将丢失。

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

于 2013-04-19T12:37:58.450 回答
6

async不在后台线程上运行您的代码。您上面的代码应该引起编译器警告,告诉您您的方法将同步运行。

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

public void ProcessMessages()
{
  ...
}

TaskEx.Run(() => ProcessMessages());
于 2013-04-19T12:17:39.010 回答