async await Behavior [重複]

Nov 19 2020

私はasync-awaitを試していましたが、少なくとも私にとっては、このかなり奇妙な動作に遭遇しました。
長時間実行されるタスクをシミュレートする3つのメソッドを作成しました。
2つのボタンクリックハンドラーについて考えてみます
。button1_clickの場合、経過時間は約6000ミリ秒でしたが、button2_clickの場合は約3000ミリ秒でした。
なぜこれが起こったのか、つまり6000ms対3000msに頭を悩ませることはできません。

    private async Task<string> TaskOne()
    {
        await Task.Delay(1000);
        return "task one";
    }

    private async Task<string> TaskTwo()
    {
        await Task.Delay(2000);
        return "task two";
    }

    private async Task<string> TaskThree()
    {
        await Task.Delay(3000);
        return "task three";
    }
    
    //time elapsed = 6000+ms
    private async void button1_Click(object sender, EventArgs e)
    {
        var watch = new Stopwatch();
        watch.Start();

        await TaskOne();
        await TaskTwo();
        await TaskThree();
        
        watch.Stop();
        textBox3.Text = watch.ElapsedMilliseconds.ToString();
    }

    //time elapsed = 3000+ms
    private async void button2_Click(object sender, EventArgs e)
    {
        var watch = new Stopwatch();
        watch.Start();

        var taskOne = TaskOne();
        var taskTwo = TaskTwo();
        var taskThree = TaskThree();

        await taskOne;
        await taskTwo;
        await taskThree;
        
        watch.Stop();
        textBox3.Text = watch.ElapsedMilliseconds.ToString();
    }

回答

7 dogyear Nov 20 2020 at 02:57

この場合:

 await TaskOne();
 await TaskTwo();
 await TaskThree();

TaskTwo()は、待っているため、TaskOne()が完了するまで開始できません。同様に、TaskThree()は、待機のためにTaskTwo()が完了するまで開始できません。

次に、次で:

var taskOne = TaskOne();
var taskTwo = TaskTwo();
var taskThree = TaskThree();

await taskOne;
await taskTwo;
await taskThree;

3つのタスクすべてを同時に開始し、それらを待機しています。そのため、最も長く実行されているタスクと同じくらいの時間がかかります。asyncawaitについてこれを理解していない人がたくさんいることに驚かれることでしょう。タスクが相互に依存していない場合は、これが最適な方法です。

7 00110001 Nov 20 2020 at 03:00

概要

ここでの持ち帰りのポイント(そしてそれ以外の場合は非常に一般的な誤解です)は、await実際には「待つ」ことを意味するということです。


await演算子(C#リファレンス)

強調鉱山

await演算子は、オペランドで表される非同期操作が完了するまで、囲んでいるasyncメソッドの評価を一時停止します。非同期操作が完了すると、await演算子は操作の結果を返します(存在する場合)。

すでに完了した操作を表すオペランドにawait演算子を適用すると、囲んでいるメソッドを中断することなく、操作の結果がすぐに返されます。

awaitオペレーターは、asyncメソッドを評価するスレッドをブロックしません。await演算子が囲んでいる非同期メソッドを一時停止すると、コントロールはメソッドの呼び出し元に戻ります。


これが何が起こっているのかです。最初の例では、各タスクを開始し、それらが連続して完了するのを待っています。つまり、次の人に何かをするように頼む前に、誰かに行って何かをして終了するように頼むようなものです。

await TaskOne();    // start, do something and wait for it
await TaskTwo();    // start, do something and wait for it
await TaskThree();  // start, do  something and wait for it

2番目の例。基本的に、3つのタスク(ホット)を開始し、それらが一度に1つずつ完了するのを待ちます。つまり、それらは同時に実行され、順番に待機します。

つまり、3人の友達に言って、何かをして、最初の友達が戻ってくるのを待ってから、2番目と3番目の友達に言います。それははるかに効率的です...前の人が戻るまで厄介な友達がぶらぶらしていません。

場合でも、2番目のタスクの完了を前にまず、あなたがまだ有効であるために待っている最初のタスクを見る前に完成した状態の第二のタスクなど

var taskOne = TaskOne();     // start, do something
var taskTwo = TaskTwo();     // start, do something
var taskThree = TaskThree(); // start, do something

// all 3 tasks are started

await taskOne;   // wait for it
await taskTwo;   // wait for it
await taskThree; // wait for it

または同様にあなたは使用することができます Task.WhenAll

提供されたすべてのタスクが完了したときに完了するタスクを作成します。

var taskOne = TaskOne();     // start, do something
var taskTwo = TaskTwo();     // start, do something
var taskThree = TaskThree(); // start, do something

// wait for them all to finish!
await Task.WhenAll(taskOne, taskTwo, taskThree);