28

async/await 自体は同時実行/並列処理とは何の関係もなく、継続渡しスタイル (CPS) の実装にすぎないというのは正しいですか? そして、実際のスレッド化は、パス/復元するSynchronizationContextインスタンスによって実行されますか?await

SynchronizationContextそれが正しければ、 について次の質問があり
ます。同じスレッドで継続が実行されることが保証されます。

ただし、スレッドのコンテキスト情報が永続化されるという保証はありますか? 、、 、 などです。フレームワーク (ASP.NET、 WinForms Name、WCF、WPF) に依存しますか? CurrentPrincipalCurrentCultureCurrentUICulture

4

2 に答える 2

43

async/await 自体は同時実行/並列処理とは関係なく、CPS の実装に過ぎないというのは正しいですか?

そうですね、async/awaitは CPS を使った書き直しなので、あなたの核となる理解は正しいです。

「並行性」と「並列性」に関しては、並行性が可能になると思います。すべて「実行中」の複数のasync操作を同時に開始できます。Task.WhenAllこれはとで簡単に実行できTask.WhenAnyます。

また、asyncそれ自体は「マルチスレッド」を意味するものではありませんが、互換性のあるTask.Run簡単なマルチスレッドを可能にしますasync

実際のスレッド化は、パス/復元を待機する SynchronizationContext インスタンスによって実行されますか?

このように考えてください: CPS の書き換えによって作成された継続は、どこかで実行する必要があります。キャプチャされた「非同期コンテキスト」を使用して、継続をスケジュールできます。

補足: キャプチャされたコンテキストは、 SynchronizationContext.Current nullでない限り、実際には です。その場合、キャプチャされたコンテキストはTaskScheduler.Currentです。

もう 1 つの重要な注意事項: コンテキストのキャプチャと復元は、実際には「awaiter」オブジェクト次第です。したがって、デフォルトではawaitTask(またはその他の組み込みの awaitable) 場合、コンテキストがキャプチャされて復元されます。しかしawait、結果が のConfigureAwait(false)場合、コンテキストはキャプチャされません。同様に、await独自のカスタム awaitable の場合、コンテキストをキャプチャしません (プログラムしない限り)。

ただし、スレッドのコンテキスト情報が永続化されるという保証はありますか? Name、CurrentPrincipal、CurrentCulture、CurrentUICulture などを意味します。

SynchronizationContextとは異なりExecutionContextます。簡単な答えは、ExecutionContext常に「フロー」、つまりCurrentPrincipalフローです (そうでない場合は、セキュリティの問題である可能性があります。これが、フローしない API がExecutionContext常に で終わる理由ですUnsafe)。

UI アプリではカルチャは流れませんが、既定ではすべてのスレッドで同じです。Name同じスレッドで (たとえば、 UI を使用してSynchronizationContext) 再開しない限り、絶対にフローしません。


さらに読むには、自分自身のasync/awaitチュートリアルから始めてから、公式のasync/ awaitFAQを読むことをお勧めします。次に、 vs .に関する Stephen Toub のブログ投稿をご覧ExecutionContextSynchronizationContextください。

私のSynchronizationContext記事も参考になるかもしれません。

于 2012-09-17T13:23:41.837 に答える
4

いいえ、async/awaitキーワードはすべて同時実行に関係しています。async/await基本的に、メソッド コードをタスクと継続にラップします。コンパイラが (Task Parallel Library を使用して) 生成した正確な変換を確認するには、一部のコード スニペットを逆アセンブルします。async/のこの翻訳はawait、以下の例と「類似」しています (ただし同一ではありません!)。

async Task<int> TaskOfTResult_MethodAsync()
{
    int hours;
    // . . .
    // Return statement specifies an integer result.
    return hours;
}

// Calls to TaskOfTResult_MethodAsync
Task<int> returnedTaskTResult = TaskOfTResult_MethodAsync();
int intResult = await returnedTaskTResult;
// or, in a single statement
int intResult = await TaskOfTResult_MethodAsync();

これはおおよそ次のように変換されます

private int Result()
{
    int hours;
    // . . .
    // Return statement specifies an integer result.
    return hours;
}

メソッドの外側でリターンを待つ場所

int? hours = null;
Task<int> task = null;
task = Task.Factory.StartNew<int>(() => Result());
task.ContnueWith(cont => 
{
    // Some task completion checking...
    hours = task.Result;
}, CancellationToken.None, 
   TaskCreationOptions.None, 
   TaskScheduler.Current);

Resultまたは、TPL コードをメソッドに配置することもできます。

private int ResultAsync()
{
    int? hours = null;
    Task<int> task = null;
    task = Task.Factory.StartNew<int>(() => 
    {
        int hours;
        // . . .
        // Return statement specifies an integer result.
        return hours;
    }, CancellationToken.None, 
       TaskCreationOptions.None, 
       TaskScheduler.Current);
    try
    {
        return task.Result;
    }
    catch (AggregateException aggEx)
    {
        // Some handler method for the agg exception.
        aggEx.Handle(HandleException); 
    }
}

SynchronizationContextasync/awateコードの同じスレッドで継続が実行されることを保証するものではありません。ただし、SynchronisationContexキーワードを介して TPL コードを使用してコンテキストを設定できます。

于 2012-09-17T10:33:24.520 に答える