web-dev-qa-db-ja.com

voidを返すこととTaskを返すことの違いは何ですか?

さまざまなC#非同期CTPサンプルを見ると、voidを返す非同期関数と、非ジェネリックTaskを返す非同期関数がいくつかあります。非同期操作が完了したときに呼び出し元にデータを返すのにTask<MyType>を返すことが便利な理由はわかりますが、Taskの戻り型を持つ関数はデータをまったく返しません。なぜvoidを返さないのですか?

120
James Cadd

SLaksとKillercamの答えは良いです。コンテキストをもう少し追加すると思いました。

最初の質問は、本質的にasyncとマークできるメソッドについてです。

asyncとしてマークされたメソッドは、voidTask、または_Task<T>_を返すことができます。それらの違いは何ですか?

非同期メソッドを返す_Task<T>_を待つことができ、タスクが完了するとTが付与されます。

非同期メソッドを返すTaskを待つことができ、タスクが完了すると、タスクの継続が実行されるようにスケジュールされます。

非同期メソッドを返すvoidを待つことはできません。それは「火と忘れ」の方法です。非同期で動作し、いつ終了したかを知る方法はありません。これは少し奇妙です。 SLaksが言うように、通常は非同期イベントハンドラーを作成するときにのみそうします。イベントが発生すると、ハンドラーが実行されます。イベントハンドラーはタスクを返さないため、イベントハンドラーから返されたタスクを誰も「待機」することはありません。通常、最初にハンドラーに制御を移すのはユーザーコードではありません。

コメントの2番目の質問は、基本的にawaitedにできることに関するものです。

どのような種類のメソッドをawaitedできますか? voidを返すメソッドをawaitedにできますか?

いいえ、ボイドを返すメソッドは待ち切れません。コンパイラはawait M()M().GetAwaiter()の呼び出しに変換します。ここで、GetAwaiterはインスタンスメソッドまたは拡張メソッドです。待機値は、待機者を取得できる値である必要があります。明らかに、voidを返すメソッドは、待機者を取得できる値を生成しません。

Task- returningメソッドは、待機可能な値を生成できます。サードパーティは、待つことができるTaskのようなオブジェクトの独自の実装を作成することを望んでおり、あなたはそれらを待つことができるでしょう。ただし、asyncvoid、または_Task<T>_以外を返すTaskメソッドを宣言することはできません。

(更新:C#の将来のバージョンによって改ざんされる可能性がある私の最後の文。非同期メソッドのタスクタイプ以外の戻りタイプを許可する提案があります。)

(更新:上記の機能により、C#7になりました。)

201
Eric Lippert

呼び出し元がタスクを待つか、継続を追加する場合。

実際、voidを返す唯一の理由は、イベントハンドラを記述しているためにcannot return Taskである場合です。

22
SLaks

TaskおよびTask<T>を返すメソッドは構成可能です。つまり、awaitメソッド内でasyncを使用できます。

asyncを返すvoidメソッドは構成できませんが、他にも2つの重要なプロパティがあります。

  1. イベントハンドラとして使用できます。
  2. これらは、「トップレベル」の非同期操作を表します。

2番目のポイントは、未処理の非同期操作のcountを維持するコンテキストを扱う場合に重要です。

ASP.NETコンテキストはそのようなコンテキストの1つです。非同期Taskメソッドから待たずに非同期voidメソッドを使用すると、ASP.NET要求が早く完了します。

別のコンテキストは、ユニットテスト用に書いたAsyncContextです(利用可能 ここ )-AsyncContext.Runメソッドは、未処理の操作カウントを追跡し、ゼロのときに戻ります。

18
Stephen Cleary

タイプTask<T>はタスク並列ライブラリ(TPL)の主力タイプで、「将来的にタイプTの結果を生成する作業/ジョブ」の概念を表します。 「将来は完了するが結果を返さない作業」という概念は、非汎用タスクタイプによって表されます。

タイプTの結果がどのように生成されるかは、特定のタスクの実装の詳細です。 TPLタスクは通常、現在のプロセスのスレッドプールからワーカースレッドにファームアウトされますが、その実装の詳細はTask<T>タイプの基本ではありません。むしろ、Task<T>は、Tを生成する高遅延操作を表すことができます。

上記のコメントに基づいて:

await式は、「この式を評価して、将来結果を生成する作業を表すオブジェクトを取得します。現在のメソッドの残りを、そのタスクの継続に関連付けられたコールバックとしてサインアップします。が生成され、コールバックがサインアップされ、即時に呼び出し元に制御が戻ります」。これは通常のメソッド呼び出しとは対照的/対照的です。つまり、「何をしているのかを記憶し、このメソッドを完全に終了するまで実行し、その後メソッドの結果を知って中断したところから始めます」。


編集:2011年10月のMSDNマガジンのEric Lippertの記事を引用する必要があります。最初にこのことを理解する上で大きな助けになったからです。

より多くの情報とホワイトページの読み込みについては、 here を参照してください。

これが助けになることを願っています。

12
MoonKnight