11

非同期メソッドでメッセージを受信したいだけです! そして私の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();
        }
    }

通常のメソッドと同じようにメソッドを呼び出しているだけで、その番号は機能しています! このコードは、async/await の代わりに BackgroundWorkers を使用していたときに機能していました

アイデア?

4

2 に答える 2

27

Stephen が書いているように、async はコードをスレッドで実行しません。さいわい、TaskFactory.FromAsync を MessageQueue.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 メソッドを配置します。

応答を待つ必要がある時間や、完了した呼び出しを最終的にどのスレッドが処理するかは保証されないため、これは理にかなっています。

トランザクションを使用するには、次のように記述します。

    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返された fromを使用していましたPeekが、これは と の間で変更された可能性がPeekありReceiveます。この場合、2 番目のメッセージは失われます。

ただし、別のクライアントがキュー内の最後のメッセージを読み取った場合、ブロックされる可能性はまだあります。これを防ぐにReceiveは、タイムアウトを短くする必要があります。

于 2013-04-19T12:37:58.450 に答える
6

asyncバックグラウンド スレッドでコードを実行しません。上記のコードにより、メソッドが同期的に実行されることを示すコンパイラ警告が発生するはずです。

バックグラウンド スレッドでメソッドを実行する場合は、次を使用しますTaskEx.Run

public void ProcessMessages()
{
  ...
}

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