5

.NET 4.5 async\await フレームワークで遊んだ後、質問があります。

このプログラムを見てみましょう ( msdn の例):

    async Task<int> AccessTheWebAsync()
    {
        HttpClient client = new HttpClient();

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

        DoIndependentWork();

        string urlContents = await getStringTask;

        return urlContents.Length;
    }


    void DoIndependentWork()
    {
        resultsTextBox.Text += "Working . . . . . . .\r\n";
    }

プログラムは次の順序で実行されます。

  1. 新しい HttpClient.
  2. GetStringAsync は同期的に呼び出します。
  3. ある時点で、GetStringAsync 呼び出しが待機し、コントロールが AccessTheWebAsync に戻ります。
  4. DoIndependentWork が呼び出されました。
  5. プログラムは文字列が返されるのを待ちます (操作が完了しなかった場合はブロックします)。
  6. urlContent の長さを返します。

理解するのに少し時間がかかったのは、メソッドGetStringAsyncがその名前にもかかわらず同期的に実行されることです (名前の規則は本当に誤解を招くものです)。

メソッドを非同期で実行するには、Task.RunorをTask.Factory.StartNew明示的に使用する必要があります。

しかし、本当の問題は、独立した作業がある場合、GetStringAsync から await が呼び出されるのを待つのではなく、なぜすぐにそれを実行しないのかということです。(つまり、定義上、非同期メソッドが非同期で実行されないのはなぜですか?)

編集: 2 番目と 3 番目の操作を言い換えます。

(2) GetStringAsync は同期的に開始します。

(3) ある時点で、GetStringAsync 呼び出しが待機し、スレッドが分岐し、制御が AccessTheWebAsync に戻ります。

4

4 に答える 4

8

理解するのに少し時間がかかったのは、メソッド GetStringAsync がその名前にもかかわらず同期的に実行されることです (名前の規則は本当に誤解を招くものです)。

それは正しくありません。 GetStringAsyncを返しますTask<string>。すぐに戻りDoIndependentWorkます。つまり、ダウンロードが完了する前に (潜在的に) 実行されます。オペレーターは、返された処理が完了awaitするまで非同期に待機します。Task<T>GetStringAsync

しかし、本当の問題は、独立した作業がある場合、GetStringAsync から await が呼び出されるのを待つのではなく、なぜすぐに作業しないのかということです。(つまり、定義上、非同期メソッドが非同期で実行されないのはなぜですか?)

非同期メソッドの実際の「作業」は、メソッドが戻った後に実行されます。Task返される は (通常) 完了していない状態になります。これは、メソッドがまだ非同期で実行されていることを意味します。確認することができますTask.IsCompleted

これを試して:

Task<string> getStringTask = client.GetStringAsync("http://msdn.microsoft.com");
Debug.WriteLine("Completed? {0}", getStringTask.IsCompleted); // Will likely print false
DoIndependentWork();
string urlContents = await getStringTask;
Debug.WriteLine("Completed now? {0}", getStringTask.IsCompleted); // Will always print true
于 2013-10-19T00:19:31.013 に答える
3

asyncメソッドは、各 でセグメントに分割されますawait。各セグメントは、コンパイラによって生成されたステート マシンの状態になります。

await命令は、 aが最も一般的なケースであるawaitableで機能します。Task

各状態/セグメントは、受信した awaitable が既に完了しているかどうかがチェックされるまで、同期的に実行されます。

awaitable が完了すると、次の状態で実行が続行されます。

awaitable が完了せず、 current がない場合、SynchronizationContext実行は awaitable が完了するまでブロックされ、その時点で次の状態の実行が開始されます。

currentSynchronizationContextが存在する場合、実行は呼び出し元に戻り、awaitable が完了すると、次の状態への継続が capture にポストされSynchronizationContextます。

于 2013-10-20T20:40:35.227 に答える
2

プログラムは文字列が返されるのを待ちます (操作が完了しなかった場合はブロックします)。

いいえ、そうではありません。await 待機しますが、スレッドをブロックしません。それが要点です。

理解するのに少し時間がかかったのは、メソッドGetStringAsyncがその名前にもかかわらず同期的に実行されることです (名前の規則は本当に誤解を招くものです)。

それは間違っている。ほとんどのメソッドは非同期で実行されます。また、最初に小さな同期部分がある可能性が最も高いですが、それは無視できるはずです。

メソッドを非同期で実行するには、Task.RunorをTask.Factory.StartNew明示的に使用する必要があります。

いいえ、メソッドはすでに非同期で実行されています。小さな同期部分を別のスレッドで実行したい場合は、 を使用できますTask.Run()が、ほとんど意味がありません。また、これStartNew()には使用しないでください。非同期メソッドではうまく機能しません。

言い換えれば、なぜ async メソッドは定義上非同期で実行されないのでしょうか?

重要な点awaitは、終了したのと同じコンテキストで再開することです。

これは、UI を変更して非同期操作を実行し、UI を再度変更することが多い GUI アプリケーションで非常に役立ちます。asyncメソッド全体がスレッドで実行されることを自動的に意味する場合ThreadPool、それは不可能です。

また、このようにすると、時間がかからないために別のスレッドに切り替える必要がないため、より効率的です。

于 2013-10-19T01:06:10.323 に答える