voidを返すこととタスクを返すことの違いは何ですか?


128

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

回答:


214

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

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

async戻り可能としてマークされたメソッドvoidTaskまたはTask<T>。それらの違いは何ですか?

Task<T>返す非同期メソッドを待つことができ、タスクが完了すると、それはT.を差し出すます

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

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

コメントの2番目の質問は、本質的には何をawait編集できるかについてです。

どのような方法でawait編集できますか?ボイドを返す方法はawait編集できますか?

いいえ、ボイドを返すメソッドを待つことはできません。コンパイラは、翻訳await M()への呼び出しにM().GetAwaiter()GetAwaiterインスタンスメソッドまたは拡張メソッドであるかもしれません。待機する値は、待機者を取得できる値でなければなりません。明らかにvoidを返すメソッドは、待機者を取得できる値を生成しません。

Task-returningメソッドは、待機可能な値を生成できます。私たちは、サードパーティがTask待つことができるようなオブジェクトの独自の実装を作成することを望み、あなたはそれらを待つことができるでしょう。ただし、宣言することはできませんasyncリターンは何もなく、その方法voidTaskまたはをTask<T>

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

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


7
+1欠けているのは、voidを返す非同期メソッドでの例外の処理方法の違いだけだと思います。
ジョアン・アンジェロ

10
@JamesCadd:いくつかの非同期作業が例外をスローするとします。誰が捕まえるの?非同期タスクを開始したコードはもはやスタック上にありません-同じスレッド上にないかもしれません-そして例外はすべてのcatch / finallyブロックがスタック上にあると仮定しています。それで、あなたは何をしますか?例外情報はタスクに保存されるため、後で検査することができます。ただし、メソッドがvoidを返す場合、ユーザーコードで使用できるタスクはありません。この状況にどの程度正確に対処するかは議論の余地のある問題であり、現時点では私たちが何を決定したのか思い出せません。
Eric Lippert、2011年

8
私は実際にビルドでスティーブン・トウブにこの質問をしました。.NET 4.0では、タスク内の監視されていない未処理の例外は、TPLが監視されていないことを検出すると、最終的にプロセスをクラッシュさせます。4.5では、デフォルトの動作を変更して、監視されていない例外がTaskScheduler :: UnobservedTaskExceptionイベントを介して引き続き報告されるようになりましたが、プロセスがクラッシュしなくなりました。あなたがいる場合したい古い4.0の動作をあなたが持つに戻って選ぶことができます。<ランタイム> <ThrowUnobservedTaskExceptionsが有効= /「真」> </ランタイム>。おそらく、変更はvoid非同期メソッドのファイアアンドフォーゲットをサポートするために正確に行われたものです。
ドリューマーシュ

4
async voidメソッドSynchronizationContextは、実行を開始したときにアクティブだったで例外を発生させます。これは、(同期)イベントハンドラーの動作に似ています。@DrewMarsh:UnobservedTaskExceptionおよびランタイム設定は、「ファイアアンドフォーゲット」非同期タスクメソッドにのみ適用され、メソッドには適用されませんasync void
Stephen Cleary 2013年

1
非同期例外処理情報の引用リンク:blogs.msdn.com/b/pfxteam/archive/2012/04/12/10293335.aspx#11
Luke Puplett

23

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

実際、戻る唯一の理由voidは、イベントハンドラを記述しているために戻ることができない場合ですTask


void型を返すメソッドを待つことは可能だと思いました-もう少し詳しく説明してもらえますか?
James Cadd

1
いいえ、できません。メソッドがを返した場合、voidそれが生成するタスクに到達する方法はありません。(実際には、それがa Taskを生成するかどうかさえ
わかり

18

メソッドは戻りTaskTask<T>合成可能です。つまりawaitasyncメソッド内でそれらを実行できます。

async返さvoidれるメソッドは合成可能ではありませんが、他に2つの重要なプロパティがあります。

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

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

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

もう1つのコンテキストは、AsyncContext単体テスト用に私が書いたものです(ここから入手できます)。このAsyncContext.Runメソッドは、未処理の操作の数を追跡し、ゼロになると戻ります。


12

タイプTask<T>は、タスクパラレルライブラリ(TPL)の主力タイプTであり、「将来的にタイプの結果を生成するいくつかの作業/ジョブ」の概念を表します。「将来は完了するが結果を返さない作業」の概念は、非ジェネリックタスクタイプで表されます。

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

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

このawait式は、「この式を評価して、将来結果を生成する作業を表すオブジェクトを取得することを意味します。現在のメソッドの残りの部分を、そのタスクの継続に関連するコールバックとして登録します。そのタスクが生成されてコールバックされると、サインアップされている場合は、すぐに制御を呼び出し元戻します。」これは、通常のメソッド呼び出しとは対照的/対照的です。つまり、「何をしているかを覚えて、完全に完了するまでこのメソッドを実行し、中断したところから再開して、メソッドの結果を把握する」ということです。


編集:2011年10月のMSDN MagazineのEric Lippertの記事を引用する必要があります。これは、最初にこのことを理解するのに非常に役立ちました。

その他の情報とホワイトページの読み込みについては、こちらをご覧ください

これがお役に立てば幸いです。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.