59

私はC#5の新しい非同期のものを検討してきましたが、1つの特定の質問が出てきました。

キーワードは、継続渡しawaitを実装するための優れたコンパイラトリック/シンタックスシュガーであることを理解しています。メソッドの残りの部分はオブジェクトに分割され、順番に実行されるようにキューに入れられますが、制御は呼び出し元のメソッドに返されます。Task

私の問題は、現在これがすべて単一のスレッド上にあると聞いたことです。これは、この非同期のものが実際には継続コードをオブジェクトに変換し、各タスクが完了した後、次のタスクを開始する前にTask呼び出す方法にすぎないことを意味しますか?Application.DoEvents()

それとも私は何かが足りないのですか?(質問のこの部分は修辞的です-私は何かが欠けていることを完全に知っています:))

4

5 に答える 5

59

多くの未処理の非同期操作がいつでも進行している可能性があるという意味で、それは並行です。マルチスレッド化されている場合とされていない場合があります。

デフォルトでawaitは、継続を「現在の実行コンテキスト」に戻すようにスケジュールします。「現在の実行コンテキスト」はSynchronizationContext.Current、それが非nullであるか、またはTaskScheduler.Current存在しないかのように定義されSynchronizationContextます。

ConfigureAwaitパラメータを呼び出して渡すことfalseで、このデフォルトの動作をオーバーライドできますcontinueOnCapturedContext。その場合、継続はその実行コンテキストにスケジュールされません。これは通常、スレッドプール スレッドで実行されることを意味します。

ライブラリ コードを作成している場合を除き、デフォルトの動作はまさに望ましい動作です。WinForms、WPF、および Silverlight (つまり、すべての UI フレームワーク) は を提供するSynchronizationContextため、継続は UI スレッドで実行されます (そして、UI オブジェクトに安全にアクセスできます)。ASP.NETSynchronizationContextは、継続が正しい要求コンテキストで実行されることを保証する も提供します。

他のスレッド (スレッドプール スレッド、Thread、およびを含むBackgroundWorker) は、 を提供しませんSynchronizationContext。そのため、既定ではコンソール アプリと Win32 サービスにはまったくありませんSynchronizationContext。この場合、継続はスレッドプール スレッドで実行されます。awaitこれが、 /を使用したコンソール アプリのデモで/asyncへの呼び出しを含めるか、.Console.ReadLineReadKeyWaitTask

が必要な場合は、私のNito.AsyncExライブラリからSynchronizationContext使用できます。基本的には、互換性のある「メインループ」と. コンソール アプリと単体テストに便利だと思います(VS2012 には単体テストのサポートが組み込まれています)。AsyncContextasyncSynchronizationContextasync Task

の詳細については、私の 2 月の MSDN 記事SynchronizationContextを参照してください。

DoEvents呼び出されることは決してありません。むしろ、制御フローは最後まで戻り、継続 (関数の残り) は後で実行されるようにスケジュールされています。これは、使用された場合のような再入可能性の問題を引き起こさないため、よりクリーンなソリューションですDoEvents

于 2011-10-05T15:25:49.307 に答える
5

async/await の背後にある全体的な考え方は、継続パスを適切に実行し、操作に新しいスレッドを割り当てないということです。継続新しいスレッドで発生する可能性があり、同じスレッドで継続する可能性があります。

于 2011-10-05T14:46:14.450 に答える
2

async/await の実際の "肉" (非同期) 部分は、通常は個別に行われ、呼び出し元への通信は TaskCompletionSource を通じて行われます。ここに書かれているようにhttp://blogs.msdn.com/b/pfxteam/archive/2009/06/02/9685804.aspx

TaskCompletionSource 型は、2 つの関連する目的を果たします。どちらもその名前から暗示されています。つまり、タスクを作成するためのソースであり、そのタスクを完了するためのソースです。基本的に、TaskCompletionSource は Task とその完了のプロデューサーとして機能します。

例は非常に明確です。

public static Task<T> RunAsync<T>(Func<T> function) 
{ 
    if (function == null) throw new ArgumentNullException(“function”); 
    var tcs = new TaskCompletionSource<T>(); 
    ThreadPool.QueueUserWorkItem(_ => 
    { 
        try 
        {  
            T result = function(); 
            tcs.SetResult(result);  
        } 
        catch(Exception exc) { tcs.SetException(exc); } 
    }); 
    return tcs.Task; 
}

を介して、待機できるオブジェクトTaskCompletionSourceにアクセスできTaskますが、マルチスレッドを作成したのは async/await キーワードを介したものではありません。

TaskCompletionSource多くの「遅い」関数が async/await 構文に変換される場合、あまり使用する必要がないことに注意してください。彼らはそれを内部で使用します(しかし、最終的にどこかにTaskCompletionSource非同期の結果が必要です)

于 2011-10-05T14:49:22.793 に答える
1

私が説明したい方法は、「await」キーワードは単にタスクが終了するのを待ちますが、待機中に呼び出し元のスレッドに実行を譲るということです。次に、Task の結果を返し、Task が完了すると、「await」キーワードの後のステートメントから続行します。

Task が呼び出し元のスレッドと同じスレッドで実行されていると考えている人もいますが、これは誤りであり、呼び出しを待機するメソッド内の Windows.Forms GUI 要素を変更しようとすることで証明できます。ただし、継続は可能な限り呼び出しスレッドで実行されます。

これは、タスクの完了時にコールバック デリゲートまたはイベント ハンドラーを使用する必要がないための優れた方法です。

于 2011-11-03T09:52:39.747 に答える