33

http://msdn.microsoft.com/en-us/library/vstudio/hh191443.aspx?cs-save-lang=1&cs-lang=csharpで非同期関数呼び出しについて読んでいました。

最初の例では、彼らはこれを行います。

Task<string> getStringTask = client.GetStringAsync("http://msdn.microsoft.com");

// You can do work here that doesn't rely on the string from GetStringAsync.
DoIndependentWork();

string urlContents = await getStringTask;

しかし、その間に行う作業がない場合は、次のようにすればよいと彼らは説明しています。

string urlContents = await client.GetStringAsync();

私が理解していることから、awaitキーワードは関数が戻るまでコードフローを中断します。では、これは次の場合とどう違うのでしょうか。

string urlContents = client.GetString();

?

4

2 に答える 2

21

呼び出しawait client.GetStringAsync()により、呼び出し元のメソッドに実行が渡されます。つまり、メソッドの実行が完了するまで待機しないため、スレッドがブロックされません。バックグラウンドでの実行が完了すると、メソッドは停止したところから続行します。

を呼び出すだけclient.GetString()では、このメソッドの実行が終了するまでスレッドの実行は続行されず、スレッドがブロックされ、UI が応答しなくなる可能性があります。

例:

public void MainFunc()
{
    InnerFunc();
    Console.WriteLine("InnerFunc finished");
}

public void InnerFunc()
{
    // This causes InnerFunc to return execution to MainFunc,
    // which will display "InnerFunc finished" immediately.
    string urlContents = await client.GetStringAsync();

    // Do stuff with urlContents
}

public void InnerFunc()
{
    // "InnerFunc finished" will only be displayed when InnerFunc returns,
    // which may take a while since GetString is a costly call.
    string urlContents = client.GetString();

    // Do stuff with urlContents
}
于 2013-06-22T11:18:51.583 に答える
14

私が理解していることから、 await キーワードは、関数が戻るまでコードフローを中断します

はい、いいえ。

  • はい、ある意味でコード フローが停止するためです。
  • いいえ、このコード フローを実行するスレッドはブロックされないためです。(同期呼び出しclient.GetString()はスレッドをブロックします)。

実際、呼び出し元のメソッドに戻ります。呼び出し元のメソッドに戻ることの意味を理解するために、別の C# コンパイラの魔法であるyield returnステートメントについて読むことができます。

イテレータ ブロックはyield return、メソッドをステート マシンに分割します。ステートメントの後のコードは、列挙子で呼び出されたyield return後にのみ実行されます。MoveNext()これこれを参照)。

現在、async/awaitメカニズムも同様のステート マシンに基づいています (ただし、ステート マシンよりもはるかに複雑ですyield return)。

簡単にするために、単純な非同期メソッドを考えてみましょう。

public async Task MyMethodAsync()
{
    // code block 1 - code before await

    // await stateement
    var r = await SomeAwaitableMethodAsync();

    // code block 2 - code after await
}
  • メソッドをasync識別子でマークすると、コンパイラにメソッドをステートマシンに分割し、awaitこのメソッド内に移動するように指示します。
  • コードがスレッドで実行されていてThread1、コードが this を呼び出しているとしますMyMethodAsync()。次にcode block 1、同じスレッドで同期的に実行されます。
  • SomeAwaitableMethodAsync()も同期的に呼び出されますが、メソッドが新しい非同期操作を開始し、Task.
  • これがawait絵になるときです。コード フローが呼び出し元に返され、スレッドThread1は呼び出し元のコードを自由に実行できます。メソッドの呼び出しで何が起こるかは、メソッドawaitの呼び出しがオンであるか、他のMyMethodAsync()何かを行うかによって異なりますが、重要なことはThread1ブロックされていないことです。
  • 残りの await の魔法 - によって返されたタスクがSomeAwaitableMethodAsync()最終的に完了すると、実行code block 2スケジュールされます。
  • async/awaitはタスク並列ライブラリ上に構築されているため、このスケジューリングは TPL で行われます。
  • 問題は、スレッド アフィニティ (WPF/WinForms UI スレッドなど)でアクティブでない限りcode block 2、同じスレッドでスケジュールされない可能性があることです。は認識されているため、が呼び出されたときに、もしあれば同じ でスケジュールされます。アクティブな がなかった場合、可能性として、別のスレッドで実行されます。Thread1SynchronizationContextawaitSynchronizationContextcode block 2SynchronizationContextMyMethodAsync()SynchronizationContextcode block 2

最後に、 はasync/awaitコンパイラによって作成されたステート マシンに基づいているためyield return、いくつかの欠点を共有しています。たとえば、ブロックawait内では実行できません。finally

これで疑問が解消されることを願っています。

于 2013-06-23T08:35:30.263 に答える