web-dev-qa-db-ja.com

TPLTaskFactory.FromAsyncとブロッキングメソッドを使用したタスク

メソッドのブロックバージョンでTPLTaskFactory.FromAsyncを使用することとTaskFactory.StartNewを使用することの間にパフォーマンスへの影響があるかどうか疑問に思いました。 100個以下の同時接続をサポートするTCPサーバーを作成しています。最初のオプションでコードを作成し、続行して複数の読み取りと書き込み操作を連鎖させた後、醜いままになりました。コードをデバッグするのは難しい。

同期バージョンでコードを記述し、それをタスクでラップすると、複雑さが軽減され、テスト容易性が向上すると思いますが、これを行うことによるパフォーマンスへの影響が心配です。

たとえば、次の2つの呼び出しの間にパフォーマンスの違いはありますか。

NetworkStream stream;
byte[] data;
int bytesRead;

//using FromAsync
Task<int> readChunk = Task<int>.Factory.FromAsync (
      stream.BeginRead, stream.EndRead,
      data, bytesRead, data.Length - bytesRead, null);

//using StartNew with blocking version
Task<int> readChunk2 = Task<int>.Factory.StartNew(() => 
      stream.Read(data, bytesRead, data.Length - bytesRead));
20

APIがメソッドのBeginXXX/EndXXXバージョンを提供する場合、絶対にFromAsyncを使用したいと考えています。違いは、StreamまたはSocketまたはWebRequestのようなものの場合、実際にはカバーの下で非同期I/Oを使用することになるということです(例:I/O Windowsの完了ポート)。これは、同期操作を実行する複数のCPUスレッドをブロックするよりもはるかに効率的です。これらの方法は、I/Oスケーラビリティを実現するための最良の方法を提供します。

非同期ニルヴァーナを実現するためにこれら2つのプログラミングモデルを組み合わせる方法の詳細については、MSDNの.NET SDKのこのセクション TPLおよび従来の.NET非同期プログラミング を確認してください。

45
Drew Marsh

外部リンクからのコピーをたどる:

はい。 .NET 4では、タスク並列ライブラリにAPMパターン(開始/終了)の組み込みラッパーであるTask.Factory.FromAsyncが含まれています。たとえば、StreamのBeginRead/EndReadメソッドを呼び出すためのタスクを作成する場合は、次のようにすることができます。

Stream s = ...;
byte [] buffer = ...;
Task<int> numBytesRead = Task<int>.Factory.FromAsync(s.BeginRead, s.EndRead, buffer, 0, buffer.Length, null);
// or with await
int numBytesRead = await Task<int>.Factory.FromAsync(s.BeginRead, s.EndRead, buffer, 0, buffer.Length, null);

裏では、FromAsyncはTaskCompletionSourceの上に構築されています。この読み取り例のFromAsyncの単純なバージョンは、次のようになります。

var tcs = new TaskCompletionSource<TResult>();
s.BeginRead(buffer, 0, buffer.Length, iar =>
{
    try { tcs.SetResult(s.EndRead(iar)); }
    catch(Exception exc) { tcs.SetException(exc); }
}, null);
Task<int> numBytesRead = tcs.Task;

http://social.msdn.Microsoft.com/Forums/en/async/thread/ed8a14​​e8-d19a-42d1-bc3f-7017bdfed09c

4
ghd