2016-06-28 8 views
0

私はAsync/Await操作の新機能です。だから私は練習でそれについてのドキュメントと研究を読む。私はこれがどのように現場の裏で働くのか分からない。明らかに非同期操作でループ中にWinformsのUIを更新できるのはなぜですか?

このコードの無料UI:

private async void button1_Click(object sender, EventArgs e) 
    { 
     while (true) 
     { 
      label1.Text = DateTime.Now.ToString(CultureInfo.InvariantCulture); // ui thread - sync 
     } 
    } 

しかし、これは自由なUIではない:Task.Delayは、UIスレッドではなくループしながら、実行するコードを残り、終わった後、私の心の中で

private async void button1_Click(object sender, EventArgs e) 
    { 
     while (true) 
     { 
      await Task.Delay(1000); // async 
      label1.Text = DateTime.Now.ToString(CultureInfo.InvariantCulture); // ui thread - sync 
     } 
    } 

レンダリングUIを停止します。私はこの時点で立ち往生しています!

私の内部で非同期メソッドを使用するとUIが更新されるのはなぜですか?

ご不明な点がございましたらお気軽にご質問ください。ありがとうございました。コードはawaitに当たると

+0

ボタンをクリックすると、スレッドはいずれの場合も無限ループに張り付きます。あなたの質問を明確にすることができますか? – Mekap

+0

私の助けてくれてありがとう、最初のケースでは私のアプリはいつも無料で、2番目のケースでは私のアプリが反応し、ラベルは毎秒1秒ごとに更新されました。 – vietvoquoc

+0

何か助けてもらえますか? – vietvoquoc

答えて

1

多くの場合、async-awaitを使用すると、ジョブは別のスレッドによって実行されると考えられることがよくあります。実際、すべての作業は1つのスレッドで行われます。このスレッドは待っているまですべての正常な作業を行います。関数が完了するのを実際に待つのではなく、呼び出し元が何かを持っていることを見るために呼び出しスタックを上がります(呼び出し元が待機していないかどうかを調べるために)、呼び出し元が待たなければならないまで文を実行します。スレッドには、それは完全なコールスタックは、元のawaitがすでに終了している場合、それはチェック待っされるとのawaitなどまで、何かを行うことができますかどうかを確認するためにコールスタックに上がります。そうであればスレッドは待ってからステートメントを続行し、そうでなければスレッドは実際にタスクが終了するまで停止します。

だから限り、どこかのスレッドで、あなたのスレッドではなく、待って、他のことを行うことができ、何のawaitはありません。

private async Task A() 
{ 
    Do1(); 
    var taskB = B(); // call B without await 
    Do2(); 
    await taskB;  // wait for B to finish 
    Do3(); 
} 

private async Task B() 
{ 
    Do4(); 
    var taskC = C(); // call C without await 
    Do5(); 
    await taskC;  // wait for C to finish 
    Do6(); 
} 

private async Task C() 
{ 
    Do7(); 
    await Task.Delay(TimeSpan.FromSeconds(0.5)); 
    Do8(); 
} 

A()が呼び出されると、Do1; B; Do4; C; Do7; Task.Delay。スレッドが最初に待機します。

async-awaitのため、スレッドはTask.DelayをCで待機しません。

代わりにスレッドが呼び出しスタックを上って、Bで何かできるかどうかを確認します。この例では、Bはまだ待機していないため、Do5が呼び出されます。今度はBで待っています。スレッドはまだ待機しませんが、プロシージャAへの呼び出しスタックに入り、Aで何かできることがあるかどうかを確認します。実際には、Bを呼び出した後にAが待機していないため、Do2は呼び出される。それからAが待っています。コールスタックの誰もがスレッドを待ってたら、スレッドが戻っTask.Delayに行くと、本当に時間が経過するまで待って起動するなど

、...見てそのコールスタックに上がります。インクルードが待った後、スレッドは、コールスタック内の誰もが待っていることを知っています。

スレッドがDO8を続行し、(それは誰もが待っていたことを知っているすべての後に)C.への呼び出しの後の最初のawaitの後に続けるために、そのコールスタックを上がります。Do6が呼び出され、スレッドはA:Do3で待機した後にステートメントに戻ります。

async-awaitは、プロシージャが呼び出される順序に影響します。したがって、結果が必要な手続きを待つ必要があります。

例でDo2がB()(つまりDo4/Do5/Do6/C/Do7/Do8)で何かの結果を必要とする場合、Do2を呼び出す前にB()を待つべきです。

複数のものが同時に実行されているように見えますが、実際はすべて同じスレッドによって別々に実行されます。

Eric Lippertは、この動作をレストランの料理人:Eric Lippert about async-awaitと比較しました。真ん中のどこかで非同期に検索してください:もしパンが乾杯するまで待つことなく、卵を調理するために水を沸騰させ始める。料理人は同時にトーストパンとトーストのパンにパンを入れませんが、ひとつ待つ代わりにトーストされたパンを必要としない他のことをやり始めます。

私にそれを理解するためにたくさん助けた非同期のawaitの別のリンク:Stephen Cleary about async and await

今すぐ戻ってあなたの質問に。

最初の例では、と書かれています。 このコードでは、UIが明らかにフリーズしています。そして確かにそうです。

2番目の例では、Task.Delayを待つときにスレッドは何もしません。代わりに、それはマウスのクリックに反応するような、何か他のことをすることができるかどうかを知るためにコールステックを上げます。それがUIを生き生きとした状態に保ちます。

+0

私には良い説明をする時間がかかりました。私はそれについて非常に感謝し、あなたが提供したリンクについてもっと研究します。 – vietvoquoc

0

は、コードがDelayタスクに継続をエンキューして、UIスレッドを残すだろう。これは、次の第二に、UIスレッドは必要なレンダリング作業を自由に行うことを意味します。

1秒が経過すると、Taskが完了し、UIスレッド(SynchronizationContextのおかげで)で継続が実行され、サイクルが繰り返されます。

+0

私の質問に答える時間をとってくれてありがとう!私はますます研究していきます。 – vietvoquoc

関連する問題