10

同時に数百の TCP 接続を確立し、それらから一定のデータ ストリームを受信するアプリケーションがあります。

 private void startReceive()
    {
        SocketAsyncEventArgs e = new SocketAsyncEventArgs();
        e.Completed += receiveCompleted;
        e.SetBuffer(new byte[1024], 0, 1024);
        if (!Socket.ReceiveAsync(e)) { receiveCompleted(this, e); }  
    }

    void receiveCompleted(object sender, SocketAsyncEventArgs e)
    {
        ProcessData(e);

        if (!Socket.ReceiveAsync(e)) { receiveCompleted(this, e); }
    }

私の試みは次のようなものになりました:

private async void StartReceive()
    {
        byte[] Buff = new byte[1024];
        int recv = 0;
        while (Socket.Connected)
        {
            recv = await NetworkStream.ReadAsync(Buff, 0, 1024);
            ProcessData(Buff,recv);
        }
    }

私が抱えていた問題は、メソッドの呼び出しStartReceive()がブロックされ、付随するStartSend() method called afterStartReceive() . Creating a new task forStartReceive() would just end up with 300-ish threads, and it seems to do so just by callingStartReceive()` に到達しないことでした。

何百ものスレッド/タスクを使用する必要を避けるために使用しているスレッドプールを使用しているときに、既存のコードにnewasyncおよびawaitキーワードを実装する正しい方法は何でしょうか?NetworkStreamSocket.SendAsync()Socket.ReceiveAsync()

networkstreamを使用した I/O 完了ポートよりも、この方法で使用することのパフォーマンス上の利点はありますbeginreceiveか?

4

1 に答える 1

26

ここでは、非同期スタイル ( SocketAsyncEventArgsto Task/ async) と抽象化のレベル ( Socketto NetworkStream) という 2 つのことを同時に変更しています。

あなたはすでに に慣れているのでSocket、非同期スタイルを変更するだけで、引き続きSocketクラスを直接使用することをお勧めします。

Async CTP は互換性のあるメソッドを提供しませんSocket(asyncこれは奇妙です。誤って取り残され、.NET 4.5 で追加されると思います)。

私のAsyncEx ライブラリを使用すれば、独自のReceiveAsyncTask拡張メソッド (および他の操作用の同様のラッパー)を作成するのはそれほど難しくありません。

public static Task<int> ReceiveAsyncTask(this Socket socket,
    byte[] buffer, int offset, int size)
{
  return AsyncFactory<int>.FromApm(socket.BeginReceive, socket.EndReceive,
      buffer, offset, size, SocketFlags.None);
}

これを行うと、次のStartReceiveように書くことができます。

private async Task StartReceive()
{
  try
  {
    var buffer = new byte[1024];
    while (true)
    {
      var bytesReceived = await socket.ReceiveAsyncTask(buffer, 0, 1024)
          .ConfigureAwait(false);
      ProcessData(buffer, bytesReceived);
    }
  }
  catch (Exception ex)
  {
    // Handle errors here
  }
}

ここで、多くのマイナーな点に対処します。

  • await新しいスレッドを生成しません。私はブログに async/await イントロを書きました。他の多くのブログと同様です。async/awaitは同時実行を許可しますが、必ずしもマルチスレッドを意味するわけではありません。
  • 何百ものスレッドが問題になる可能性があります。ただし、何百ものタスクがあってもまったく問題ありません。スレッド プールと BCL は、非常に多くのタスクを処理するように設計されています。
  • async/awaitはまったく新しい形式の非同期処理ではありません。非同期処理を表現する簡単な方法です。その下ではまだ IOCP を使用しています。 /低レベルのメソッドよりもパフォーマンスがわずかに低下します。その魅力は、非同期メソッドの記述と構成が容易なことです。asyncawait
  • async非常にビジーなシステムでは、 /に切り替わると GC 圧力が上昇することがありますawait。Parallel Team の Stephen Toub が、この問題に役立つソケット固有の awaitable の例をいくつか書いています。(最初は簡単なパターンを使用し、必要に応じてパフォーマンスを向上させるアプローチのみを使用することをお勧めします。それでも、最終的に必要になった場合は、そこにあることを知っておくとよいでしょう)。
  • Task本当に返す必要がない限り、非同期メソッドは返す必要がありますvoidTask待機可能であるため、メソッドは構成可能です (そして、より簡単にテストできます)。void「ファイアアンドフォーゲット」のようなものです。
  • ConfigureAwait(false)メソッドの残りの部分を呼び出しasyncて、スレッド プール スレッドで実行するように指示できます。上記の例でこれを使用しているため、 をProcessData使用したときと同じように、スレッド プール スレッドで実行されますSocketAsyncEventArgs
  • Socket.Connected役に立たない。接続がまだ有効かどうかを検出するには、データを送信する必要があります。
于 2012-03-17T02:43:54.660 に答える