2013-04-19 68 views
10

メッセージを非同期メソッドで受信したいだけです。私のUIがフリーズしていますMessageQueueとAsync/Await

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を使用していたときに使用されました。

アイデア?

+2

コードで警告が生成されていますか?彼らが言うことを読んで理解してみましたか? – svick

+0

エラーリストを見ると、「この非同期メソッドはオペレータを待っていて、同期して実行されていません」と表示されます。 –

+1

これはローカライズされた質問ではありません。彼らのメソッドを非同期的に実行させます –

答えて

24

スティーブンが書いているように、非同期スレッドでコードを実行されません。幸いなことに、あなたは非同期メッセージを受信するためにMessageQueue.BeginReceive /MessageQueue.EndReceiveでTaskFactory.FromAsyncを使用することができます。

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を呼び出して、peek操作用に作成したイベントハンドラ内にトランザクションメソッドと(同期)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; 
    } 

UPDATEをロブが指摘したように、元のコードが使用さ

Peekの間で変更された可能性があるPeekから返さmessage、およびReceive。この場合、2番目のメッセージは失われます。

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

+1

この回答は素晴らしいです!それは動作します! このMyAsyncReceiveメソッドを複数回実行すると、スレッドを共有できますか?だから、彼らは遅くなるでしょうか? – Fraga

+1

このメソッドには特別なことはありません。ランタイムは、呼び出しごとにスレッドプールから使用可能なスレッドを使用します。 –

+0

これに競合状態は含まれていませんか? 'message'変数には受信したメッセージとは異なるメッセージが含まれていますか? (特に複数の消費者がいる場合)。 peekは* a *メッセージを返しますが、 'Receive()'メソッドは別のメッセージを受け取ることがあります。私は 'Peek awaiter'に 'var message ='を落とし、 'var message = queue.Receive(tx);'を使って、正しいメッセージがトランザクション内で処理されていることを確認します。 – RobIII

6

async does not run your code on a background thread.上記のコードでは、メソッドが同期して実行されることを示すコンパイラの警告が表示されるはずです。

あなたがTaskEx.Runを使用して、バックグラウンドスレッドでメソッドを実行する場合:

public void ProcessMessages() 
{ 
    ... 
} 

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

私がrunを使うと、新しいスレッドが生成されます!私は直接UIを変更することはできませんので、私はディスパッチリグを使用する必要がありますか?普通のスレッドのように?それとも他の方法がありますか?メインフォームにある進行状況バーを更新するだけです。 – Fraga

+0

あなたのためにスレッドディスパッチを処理する 'IProgress 'と 'Progress 'を使うことをお勧めします。 [TAPドキュメントは 'IProgress '](http://msdn.microsoft.com/en-us/library/hh873175.aspx)をカバーしています。 –

+0

私は2つの答えを正しくマークすることはできません!私の質問の答えは、Task.Factory.FromAsyncを使用していますが、私は新しいスレッドの力を使用しません。だから私は実行を使用します!ありがとう – Fraga

関連する問題