6

最近、新しい非同期CTPをいじってみましたが、どうすればよいかわからないという状況に遭遇しました。

現在のコードベースでは、「ジョブ」と「ジョブマネージャー」の概念を使用しています。ジョブは、最初のメッセージを処理し、応答を送信して、応答を待機することのみを目的として存在します。

ネットワークスレッドがデータの到着を待機し、それをイベントハンドラーに渡し、最終的にはジョブマネージャーに渡す、同期ソケットに基づく既存のコードがすでにあります。

ジョブマネージャは、メッセージを処理するジョブを探し、メッセージを渡します。

したがって、シナリオは次のとおりです。

  1. ジョブマネージャは新しいメッセージを受け取り、ジョブを起動します。
  2. ジョブが開始され、メッセージが処理され、応答メッセージが送信されます。
  3. この時点で、ジョブは応答への応答を待ちます。

擬似コードの例を次に示します。

class MyJob : Job
{
    public override void RunJob( IPacketMsg packet )
    {
        // handle packet

        var myReply = new Packet();
        SendReply( myReply );

        await GetResponse();
    }
}

しかし、ステップ3でどのように進めるかは完全にはわかりません。ジョブマネージャーは応答を取得し、実行中のジョブに渡します。しかし、ジョブに応答を待たせる方法がわかりません。

WaitHandleをブロックするだけの待機中のタスクを作成することを検討しましたが、これが最善の解決策ですか?

この場合、他にできることはありますか?

編集 非同期CTPに関して、UIが使用されていない状況で何が起こるか。Eric LippertのAsyncブログを読んだことがありますが、UIスレッドなしですべてがバックグラウンドでどのように機能するかというテーマに触れたことはないと思います(バックグラウンドワーカーからスピンオフするのでしょうか...?)

4

3 に答える 3

5
  1. ジョブ マネージャーが新しいメッセージを受け取り、ジョブを起動します。
  2. ジョブが開始され、メッセージが処理され、応答メッセージが送信されます。
  3. この時点で、ジョブは応答に対する応答を待ちます。

まず、Async CTP は非同期操作を非常にうまく処理しますが、非同期イベントはあまり処理しないことに注意してください。Rx ベースのアプローチを検討することをお勧めします。しかし、ここでは Async CTP について説明しましょう。

タスクを作成するには、次の 2 つの基本的なオプションがあります。

  • デリゲート付き。たとえば、Task.Factory.StartNewスレッド プールでデリゲートを実行します。カスタム タスク ファクトリとスケジューラを使用すると、タスク デリゲートのオプションが増えます (たとえば、デリゲートを STA スレッドで実行するように指定するなど)。
  • デリゲートなし。たとえば、TaskFactory.FromAsync既存のBegin/EndメソッドのペアをラップしTaskEx.FromResult、「将来の定数」を返し、明示的TaskCompletionSourceに制御するために使用できます(両方とも内部的に使用します)。TaskFromAsyncFromResultTCS

ジョブ処理が CPU バウンドの場合は、それを に渡すのが理にかなっていTask.Factory.StartNewます。ジョブ処理は CPU バウンドであると仮定します。

ジョブ マネージャーの擬似コード:

// Responds to a new message by starting a new job on the thread pool.
private void RespondToNewMessage(IPacketMsg message)
{
  IJob job = ..;
  Task.Factory.StartNew(job.RunJob(message));
}

// Holds tasks waiting for a response.
private ConcurrentDictionary<int, TaskCompletionSource<IResponse>> responseTasks = ..;

// Asynchronously gets a response for the specified reply.
public Task<IResponse> GetResponseForReplyAsync(int replyId)
{
  var tcs = new TaskCompletionSource<IResponse>();
  responseTasks.Add(replyId, tcs);
  return tcs.Task;
}

// Responds to a new response by completing and removing its task.
private void RespondToResponse(IResponse response)
{
  var tcs = responseTasks[response.ReplyId];
  responseTasks.Remove(response.ReplyId);
  tcs.TrySetComplete(response);
}

アイデアは、ジョブ マネージャーが未解決の応答のリストも管理するというものです。これを実現するためにint、ジョブ マネージャーがどの応答とどの応答が一致するかを判断するために使用できる単純な応答識別子を導入しました。

ジョブは次のように機能するようになりました。

public override void RunJob(IPacketMsg packet)
{
  // handle packet
  var myReply = new Packet();
  var response = jobManager.GetResponseForReplyAsync(myReply.ReplyId);
  SendReply(myReply);

  await response;
}

ジョブをスレッド プール スレッドに配置しているため、いくつか注意が必要な点があります。

  1. GetResponseForReplyAsync応答が送信される前に呼び出され(タスクを登録する)必要があり、await後で編集されます。これは、返信が送信され、登録する前に応答が受信されるという状況を回避するためです。
  2. RespondToResponseタスクを完了すると同じ ID で別の返信が送信される場合に備えて、完了する前にタスク登録を削除します。

ジョブがスレッド プール スレッドに配置する必要がないほど短い場合は、ソリューションを単純化できます。

于 2011-09-12T21:02:35.037 に答える
3

非同期CTPに関して、UIが使用されていない状況で何が起こるか。Eric LippertのAsyncブログを読んだことがありますが、UIスレッドなしですべてがバックグラウンドでどのように機能するかというテーマに触れたことはないと思います(バックグラウンドワーカーからスピンオフするのでしょうか...?)

await同期コンテキストに戻ります。UIプロセスでは、これはUIメッセージループです。ASP.NETでは、これはASP.NETスレッドプールです。他の状況(コンソールアプリケーションおよびWin32サービス)では、コンテキストがないため、継続はキューに入れられThreadPoolます。これは通常望ましい動作ではないため、このようAsyncContextな状況で使用できるクラスを作成しました。

BackgroundWorker使用されません。あなたのようなサーバー側のシナリオでは、バックグラウンドスレッドがまったくないことも珍しくありません。

于 2011-09-12T20:17:33.903 に答える
1

次のように、イベント ハンドラーの残りの部分を await パターンと結び付けるだけです。

 public async void RunJob(IPacketMsg msg)
 {
     // Do Stuff

     var response = await GetResponse();

     // response is "string", not "Task<string>"

     // Do More Stuff
 }

 public Task<string> GetResponse()
 {
     return Task.Factory.StartNew(() =>
        {
             _networkThingy.WaitForDataAvailable();

             return _networkThingy.ResponseString;
        });
 }

応答の取得タスクが完了すると、メソッドの残りの部分が現在の同期コンテキストで実行されます。ただし、それまではメソッドの実行が中断されます (したがって、GetResponse で開始されたタスクが終了するまで、待機後のコードは実行されません)。

于 2011-09-12T20:24:31.910 に答える