1年後に追加
async-await を 1 年以上使用した後、元の回答で書いた async に関するいくつかのことが正しくないことがわかりましたが、回答のコードはまだ正しいです。Hera は、async-await の仕組みを理解するのに大いに役立った 2 つのリンクです。
このインタビュー Eric Lippert は、 async-await の優れた例えを示しています。途中で async-await を検索します。
この記事では、非常に役立つ Eric Lippert が、async-await の優れたプラクティスを示しています。
元の答え
OK、これは学習プロセス中に私を助けた完全な例です。
遅い電卓があり、ボタンを押すときにそれを使用したいとします。その間、UI の応答性を維持し、他のことを行うことも必要です。電卓が終了したら、結果を表示します。
そしてもちろん、これには async / await を使用し、イベントフラグの設定やこれらのイベントが設定されるのを待つなどの古い方法は使用しません。
遅い計算機は次のとおりです。
private int SlowAdd(int a, int b)
{
System.Threading.Thread.Sleep(TimeSpan.FromSeconds(5));
return a+b;
}
async-await の使用中にこれを非同期で使用する場合は、Task.Run(...) を使用して非同期で開始する必要があります。の戻り値Task.Run
は、待機可能な Task です。
Task
実行した関数の戻り値が void の場合
Task<TResult>
実行した関数の戻り値がTResult
Task を開始するだけで、別のことを実行でき、入力した Task の結果が必要なときはいつでも await を実行できます。欠点が 1 つあります。
「待機」したい場合は、関数を非同期にして、Task
代わりにvoid
またはTask<TResult>
代わりに返す必要がありTResult
ます。
遅い電卓を実行するコードは次のとおりです。非同期関数の識別子を async で終了するのが一般的です。
private async Task<int> SlowAddAsync(int a, int b)
{
var myTask = Task.Run ( () => SlowAdd(a, b));
// if desired do other things while the slow calculator is working
// whenever you have nothing to do anymore and need the answer use await
int result = await myTask;
return result;
}
補足: 一部の人々は、Start.Run よりも Task.Factory.StartNew を好みます。これについて MSDN が伝えていることを参照してください。
MSDN: Task.Run と Task.Factory.StartNew の比較
SlowAdd は非同期関数として開始され、スレッドが続行されます。答えが必要になると、タスクを待ちます。戻り値は TResult で、この場合は int です。
実行する意味がない場合、コードは次のようになります。
private async Task`<int`> SlowAddAsync(int a, int b)
{
return await Task.Run ( () => SlowAdd(a, b));
}
SlowAddAsync は非同期関数として宣言されているため、この非同期関数を使用するすべての人も非同期で、Task または Task <TResult
>を返す必要があることに注意してください。
private async Task UpdateForm()
{
int x = this.textBox1.Text;
int y = this.textBox2.Text;
int sum = await this.SlowAddAsync(x, y);
this.label1.Text = sum.ToString();
}
async / await の良いところは、前のタスクが完了するまで待つために ContinueWith をいじる必要がないことです。await を使用するだけで、タスクが終了し、戻り値があることがわかります。await の後のステートメントは、ContinueWith で通常行うことです。
ところで、Task.Run は関数を呼び出す必要はありません。ステートメント ブロックを配置することもできます。
int sum = await Task.Run( () => {
System.Threading.Thread.Sleep(TimeSpan.FromSeconds(5));
return a+b});
ただし、別の関数の良い点は、非同期を必要としない/望んでいない/理解していない人に、非同期/待機なしで関数を使用する可能性を与えることです。
覚えて:
await を使用するすべての関数は非同期にする必要があります
すべての非同期関数は Task または Task <Tresult
>を返す必要があります
「しかし、私のイベント ハンドラーはタスクを返すことができません!」
private void OnButton1_Clicked(object sender, ...){...}
その通りです。したがって、それが唯一の例外です。
非同期イベント ハンドラが void を返す場合がある
そのため、ボタンをクリックすると、非同期イベント ハンドラーによって UI の応答性が維持されます。
private async void OnButton1_Clicked(object sender, ...)
{
await this.UpdateForm();
}
ただし、イベント ハンドラー async を宣言する必要があります。
<TResult
多くの .NET 関数には、Task または Task >を返す非同期バージョンがあります。
- インターネット アクセス - ストリームの読み取りと書き込み - データベース アクセス - などの非同期関数があります。
それらを使用するには、Task.Run を呼び出す必要はありません。既に Task と Task を返し<TResult
ます。それらを呼び出すだけで、独自の処理を続行し、答えが必要な場合は Task を待って TResult を使用します。
複数のタスクを開始し、それらが終了するのを待つ 複数のタスク
を開始し、それらすべてが終了するのを待ちたい場合は、Task.WhenAll(...) NOT Task.Waitを使用します
Task.Wait は void を返します。Task.WhenAll は Task を返すので、それを待つことができます。
タスクが終了すると、戻り値はすでに await の戻り値ですが、await の場合は Task.WhenAll ( new Task[]{TaskA, TaskB, TaskC}); タスクの結果を知るには、Task <TResult
>.Result プロパティを使用する必要があります。
int a = TaskA.Result;
タスクの 1 つが例外をスローすると、AggregateException で InnerExceptions としてラップされます。したがって、Task.WhenAll を待機する場合は、AggregateException をキャッチし、innerExceptions をチェックして、開始したタスクによってスローされたすべての例外を確認する準備をしてください。関数 AggregateException.Flatten を使用して、より簡単に例外にアクセスします。
キャンセルについて読むのは興味深い:
マネージド スレッドでのキャンセルに関する MSDN
最後に: Thread.Sleep(...) を使用します。非同期バージョンは Task.Delay(TimeSpan) です。
private async Task`<int`> MySlowAdd(int a, int b)
{
await Task.Delay(TimeSpan.FromSeconds(5));
return a+b;
}
この関数を使用すると、プログラムの応答性が維持されます。