43

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これはandの適切で適切な使用awaitですか、それとも使いすぎですか?

4

3 に答える 3

44

すでに2つの良い答えがありますが、0.02を追加するには...

非同期操作の使用について話している場合、 async/awaitは I/O バウンドと CPU バウンドの両方でうまく機能します。

MSDN のドキュメントは、非同期操作の生成に少し傾いていると思います。その場合TaskCompletionSource、I/O バウンドおよびTask.Run(または類似の) CPU バウンドに (または同様の) を使用する必要があります。Task初期ラッパーを作成したら、およびで使用するのが最適です。asyncawait

あなたの特定の例では、実際にはどれくらいの時間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
}

ただし、ConfigureAwait@ svickが言及したように、私はお勧めします:

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 のビデオMSDN の記事をご覧ください。彼は有益な情報をたくさん持っています。

于 2013-02-15T20:20:46.957 に答える
21

awaitこれは、非同期の (つまり、 で表される) 操作に適していTaskます。

Task.Run重要な点は、IO 操作では、可能な限り、ブロッキング同期メソッドで使用するのではなく、提供されたメソッドを使用することです。これは、非常にコアで非同期です。IO の実行中にスレッド (スレッド プールのスレッドであっても) をブロックしている場合は、awaitモデルの真の力を活用していません。

操作を表すを作成するTaskと、それが CPU バウンドか IO バウンドかを気にする必要がなくなります。await呼び出し元にとっては、 -edが必要な非同期操作にすぎません。

于 2013-02-15T15:39:03.083 に答える
20

考慮すべき点がいくつかあります。

  • GUI アプリケーションでは、UI スレッドで実行するコードをできるだけ少なくする必要があります。その場合、CPU バウンド操作を別のスレッドにオフロードTask.Run()することをお勧めします。コードのユーザーは、必要に応じて自分でそれを行うことができます。
  • ASP.NET アプリケーションのようなものでは、UI スレッドはなく、気にするのはパフォーマンスだけです。その場合、Task.Run()コードを直接実行する代わりに使用することで多少のオーバーヘッドが発生しますが、操作に実際に時間がかかる場合はそれほど重要ではありません。(また、同期コンテキストに戻る際にいくらかのオーバーヘッドが発生します。これが、ライブラリ コードConfigureAwait(false)でほとんどの sを使用する必要があるもう 1 つの理由です。)await
  • メソッドが async の場合 (戻り値の型だけでなく、メソッドの名前にも反映される必要があります)、人々は、CPU バウンドの作業であっても、同期コンテキスト スレッドをブロックしないことを期待します。

await Task.Run()それを重視して、ここでは使用するのが正しい選択だと思います。多少のオーバーヘッドはありますが、重要な利点もあります。

于 2013-02-15T16:03:33.437 に答える