MSDNのドキュメントには、async
およびawait
がIOにバインドされたタスクに適しているのに対し、Task.Run
は、CPUバウンドタスクに使用する必要があります。
私はHTTPリクエストを実行してHTMLドキュメントを取得し、それを解析するアプリケーションに取り組んでいます。私はこのようなメソッドを持っています:
public async Task<HtmlDocument> LoadPage(Uri address)
{
using (var httpResponse = await new HttpClient().GetAsync(address)) //IO-bound
using (var responseContent = httpResponse.Content)
using (var contentStream = await responseContent.ReadAsStreamAsync())
return await Task.Run(() => LoadHtmlDocument(contentStream)); //CPU-bound
}
これはasync
とawait
の適切な使用ですか、それとも使いすぎですか?
すでに2つの良い答えがありますが、私の0.02を追加するには...
消費非同期操作について話している場合、async
/await
はI/Oバウンドの両方でうまく機能しますとCPUバウンド。
MSDNドキュメントには、producing非同期操作に少し傾いていると思います。この場合、TaskCompletionSource
(または同様の)I/Oバウンドの場合、およびCPUバウンドの場合はTask.Run
(または類似)。最初のTask
ラッパーを作成したら、async
とawait
によるconsumedが最適です。
あなたの特定の例では、それは本当にLoadHtmlDocument
にかかる時間にかかっています。 Task.Run
を削除すると、LoadPage
を呼び出すのと同じコンテキスト内で(おそらくUIスレッド上で)実行されます。 Windows 8のガイドラインでは、50ミリ秒以上かかる操作はすべてasync
...にする必要があると規定しています...開発者のマシンでの50ミリ秒は、クライアントのマシンでは長くなる可能性があることに注意してください...
したがって、LoadHtmlDocument
の実行時間が50ミリ秒未満であることを保証できる場合は、直接実行できます。
public async Task<HtmlDocument> LoadPage(Uri address)
{
using (var httpResponse = await new HttpClient().GetAsync(address)) //IO-bound
using (var responseContent = httpResponse.Content)
using (var contentStream = await responseContent.ReadAsStreamAsync()) //IO-bound
return LoadHtmlDocument(contentStream); //CPU-bound
}
ただし、@ svickが言及したように、私はConfigureAwait
をお勧めします。
public async Task<HtmlDocument> LoadPage(Uri address)
{
using (var httpResponse = await new HttpClient().GetAsync(address)
.ConfigureAwait(continueOnCapturedContext: false)) //IO-bound
using (var responseContent = httpResponse.Content)
using (var contentStream = await responseContent.ReadAsStreamAsync()
.ConfigureAwait(continueOnCapturedContext: false)) //IO-bound
return LoadHtmlDocument(contentStream); //CPU-bound
}
ConfigureAwait
を使用すると、HTTP要求がすぐに(同期的に)完了しない場合、これにより(この場合)LoadHtmlDocument
がTask.Run
への明示的な呼び出しなしにスレッドプールスレッドで実行されます。 。
このレベルでasync
のパフォーマンスに興味がある場合は、この件に関するStephen Toubの video および MSDNの記事 を確認してください。彼はたくさんの有用な情報を持っています。
非同期の操作(つまり、await
で表される)をTask
するのが適切です。
重要な点は、IO操作の場合、可能な場合は常に、Task.Run
ブロッキング同期メソッド。 IOの実行中にスレッド(スレッドプールスレッドを含む)をブロックしている場合、await
モデルの真の力を活用していません。
操作を表すTask
を作成すると、それがCPUであるか、IOバインドされているかは関係ありません。呼び出し元にとっては、await
- ed。
考慮すべき点がいくつかあります。
Task.Run()
を使用してCPUにバインドされた操作を別のスレッドにオフロードすることをお勧めします。コードのユーザーは、必要に応じて自分でそれを行うことができます。Task.Run()
を使用すると多少のオーバーヘッドが発生しますが、操作に実際に時間がかかる場合は重要ではありません。 (また、同期コンテキストに戻る際にオーバーヘッドが発生します。これが、ライブラリコードでほとんどのawait
sにConfigureAwait(false)
を使用する必要があるもう1つの理由です。)これに重みを付けるには、await Task.Run()
を使用するのが正しい選択だと思います。オーバーヘッドはありますが、重要な利点もいくつかあります。