さまざまなC#非同期CTPサンプルを見るvoid
と、を返す非同期関数と、非ジェネリックを返す関数がありTask
ます。Task<MyType>
非同期操作が完了したときに呼び出し元にデータを返すのにa を返すと便利な理由がわかりますが、これまでに見た関数の戻り値の型にTask
は、データを返さないものがあります。なぜ戻りませんvoid
か?
さまざまなC#非同期CTPサンプルを見るvoid
と、を返す非同期関数と、非ジェネリックを返す関数がありTask
ます。Task<MyType>
非同期操作が完了したときに呼び出し元にデータを返すのにa を返すと便利な理由がわかりますが、これまでに見た関数の戻り値の型にTask
は、データを返さないものがあります。なぜ戻りませんvoid
か?
回答:
SLaksとKillercamの答えは良いです。もう少しコンテキストを追加すると思いました。
最初の質問は基本的に、どのメソッドをマークできるかについてasync
です。
async
戻り可能としてマークされたメソッドvoid
、Task
またはTask<T>
。それらの違いは何ですか?
Task<T>
返す非同期メソッドを待つことができ、タスクが完了すると、それはT.を差し出すます
Task
返す非同期メソッドを待つことができ、タスクの完了は、タスクの継続を実行するようにスケジュールされている場合。
void
返す非同期メソッドを待つことはできません。「ファイアアンドフォーゲット」方式です。非同期で動作し、いつ完了したかを知る方法はありません。これは少し奇妙なことです。SLaksが言うように、通常は非同期イベントハンドラーを作成するときにのみそれを行います。イベントが発生すると、ハンドラーが実行されます。イベントハンドラーはタスクを返さないため、誰もイベントハンドラーによって返されるタスクを「待つ」ことはできません。通常、そもそもハンドラーに制御を移すのはユーザーコードではありません。
コメントの2番目の質問は、本質的には何をawait
編集できるかについてです。
どのような方法で
await
編集できますか?ボイドを返す方法はawait
編集できますか?
いいえ、ボイドを返すメソッドを待つことはできません。コンパイラは、翻訳await M()
への呼び出しにM().GetAwaiter()
、GetAwaiter
インスタンスメソッドまたは拡張メソッドであるかもしれません。待機する値は、待機者を取得できる値でなければなりません。明らかにvoidを返すメソッドは、待機者を取得できる値を生成しません。
Task
-returningメソッドは、待機可能な値を生成できます。私たちは、サードパーティがTask
待つことができるようなオブジェクトの独自の実装を作成することを望み、あなたはそれらを待つことができるでしょう。ただし、宣言することはできませんasync
リターンは何もなく、その方法void
、Task
またはをTask<T>
。
(更新:私の最後の文では、C#の将来のバージョンによって改ざんされる可能性があります。非同期メソッドのタスクタイプ以外の戻り値のタイプを許可する提案があります。)
(更新:上記の機能により、C#7に組み込まれました。)
async void
メソッドSynchronizationContext
は、実行を開始したときにアクティブだったで例外を発生させます。これは、(同期)イベントハンドラーの動作に似ています。@DrewMarsh:UnobservedTaskException
およびランタイム設定は、「ファイアアンドフォーゲット」非同期タスクメソッドにのみ適用され、メソッドには適用されませんasync void
。
呼び出し元がタスクで待機するか、継続を追加する場合。
実際、戻る唯一の理由void
は、イベントハンドラを記述しているために戻ることができない場合ですTask
。
void
それが生成するタスクに到達する方法はありません。(実際には、それがa Task
を生成するかどうかさえ
メソッドは戻りTask
、Task<T>
合成可能です。つまりawait
、async
メソッド内でそれらを実行できます。
async
返さvoid
れるメソッドは合成可能ではありませんが、他に2つの重要なプロパティがあります。
2番目のポイントは、未処理の非同期操作の数を維持するコンテキストを扱うときに重要です。
ASP.NETコンテキストはそのようなコンテキストの1つです。非同期Task
メソッドからの待機をせずに非同期メソッドを使用するとvoid
、ASP.NET要求が早く完了します。
もう1つのコンテキストは、AsyncContext
単体テスト用に私が書いたものです(ここから入手できます)。このAsyncContext.Run
メソッドは、未処理の操作の数を追跡し、ゼロになると戻ります。
タイプTask<T>
は、タスクパラレルライブラリ(TPL)の主力タイプT
であり、「将来的にタイプの結果を生成するいくつかの作業/ジョブ」の概念を表します。「将来は完了するが結果を返さない作業」の概念は、非ジェネリックタスクタイプで表されます。
タイプの結果がどのようT
に生成されるか、および特定のタスクの実装の詳細は正確です。作業はローカルマシンの別のプロセスや別のスレッドなどにファームアウトされる可能性があります。TPLタスクは通常、現在のプロセスのスレッドプールからワーカースレッドにファームアウトされますが、その実装の詳細はTask<T>
タイプの基本ではありません。むしろ、は、Task<T>
を生成する高遅延オペレーションを表すことができT
ます。
上記のコメントに基づいて:
このawait
式は、「この式を評価して、将来結果を生成する作業を表すオブジェクトを取得することを意味します。現在のメソッドの残りの部分を、そのタスクの継続に関連するコールバックとして登録します。そのタスクが生成されてコールバックされると、サインアップされている場合は、すぐに制御を呼び出し元に戻します。」これは、通常のメソッド呼び出しとは対照的/対照的です。つまり、「何をしているかを覚えて、完全に完了するまでこのメソッドを実行し、中断したところから再開して、メソッドの結果を把握する」ということです。
編集:2011年10月のMSDN MagazineのEric Lippertの記事を引用する必要があります。これは、最初にこのことを理解するのに非常に役立ちました。
その他の情報とホワイトページの読み込みについては、こちらをご覧ください。
これがお役に立てば幸いです。