web-dev-qa-db-ja.com

`Task.Run()`が冗長な `async`ラムダを使用していますか?

私はちょうど次のようなコードに出くわしました:

_var task = Task.Run(async () => { await Foo.StartAsync(); });
task.Wait();
_

(いいえ、Foo.StartAsync()の内部構造はわかりません)。私の最初の反応はasync/awaitを取り除き、次のように書き直します。

_var task = Foo.StartAsync();
task.Wait();
_

それは正しいでしょうか、そうではありませんか(ここでも、Foo.StartAsync()についてまったく何も知りません)。 This に答える 違いは何ですか-Task.Runで 'async'アクションデリゲートを実行する... 意味があるかもしれない場合があることを示しているようです、しかしそれはまた「実を言うと、私はそんなに多くのシナリオを見たことがありません...」

20
Ðаn

通常意図された_Task.Run_の使用法は実行することです非UIスレッドのCPUバインドコード。そのため、asyncデリゲートと共に使用することは非常にまれですが、それは可能です(たとえば、非同期部分とCPUバインド部分の両方を持つコードの場合)。

ただし、それは意図された使用法です。私はあなたの例で考える:

_var task = Task.Run(async () => { await Foo.StartAsync(); });
task.Wait();
_

元の作者が非同期コードで同期的にブロックしようとしている可能性がはるかに高く、(ab)_Task.Run_を使用して その状況で一般的なデッドロックを回避する (私のブログで説明しているように) 。

本質的には、私が brownfield非同期コードに関する記事 で説明した「スレッドプールハック」のように見えます。

bestソリューションは、_Task.Run_またはWaitを使用しないことです:

_await Foo.StartAsync();
_

これにより、asyncがコードベース全体に拡大するようになります。これは最善の方法ですが、現在、開発者にとって許容できない量の作業が発生する可能性があります。これがおそらく、前任者がTask.Run(..).Wait()を使用した理由です。

13
Stephen Cleary

主にそうです。

このようなTask.Runの使用は、非同期メソッドの実行方法を理解していない人が主に使用します。

ただし、違いがあります。 Task.Runの使用は、ThreadPoolスレッドで非同期メソッドを開始することを意味します。

これは、asyncメソッドの同期部分(最初のawaitの前の部分)が大きく、呼び出し側がそのメソッドがブロックされていないことを確認したい場合に役立ちます。

これは、たとえばSynchronizationContextがない場合など、現在のコンテキストから「抜け出す」ためにも使用できます。

6
i3arnon