28

我是否正确认为 async/await 本身与并发/并行性无关,只不过是延续传递样式(CPS)实现?真正的线程是由传递/恢复的SynchronizationContext实例执行的吗?await

如果这是正确的,我有以下问题SynchronizationContext
它保证将在同一个线程上执行延续。

但是,是否有任何保证线程的上下文信息是持久的?我的意思是Name, CurrentPrincipal, CurrentCulture,CurrentUICulture等。它是否依赖于框架(ASP.NET、WinForms、WCF、WPF)?

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.

另一个重要的注意事项:上下文的捕获和恢复实际上取决于“等待者”对象。因此,默认情况下,如果您awaitTask(或任何其他内置的可等待对象),则将捕获并恢复上下文。但是,如果您await是 的结果ConfigureAwait(false),则不会捕获上下文。同样,如果await您自己自定义可等待对象,它不会捕获上下文(除非您对其进行编程)。

但是,是否有任何保证线程的上下文信息是持久的?我的意思是 Name、CurrentPrincipal、CurrentCulture、CurrentUICulture 等。

SynchronizationContext不同于ExecutionContext. 一个简化的答案是ExecutionContext总是“流动”,所以CurrentPrincipal流动(如果没有,这可能是一个安全问题,这就是为什么不流动的 APIExecutionContext总是以 结尾Unsafe)。

在 UI 应用程序中,文化不会流动,但默认情况下,无论如何它对于所有线程都是相同的。Name绝对不会流动,除非您在同一个线程上恢复(例如,使用 UI SynchronizationContext)。


对于一些进一步的阅读,我建议从我自己的async/await教程开始,然后是官方async/ awaitFAQ。然后看看Stephen Toub 关于vs.的博客文章ExecutionContextSynchronizationContext

您可能还会发现我的SynchronizationContext文章很有帮助。

于 2012-09-17T13:23:41.837 回答
4

不,async/await关键字与并发性有关。async/await基本上将您的方法代码包装到任务和延续中。要查看编​​译器生成的确切翻译(使用任务并行库),请反汇编一些代码片段。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);

或者,您可以将 TPL 代码放入Result方法中

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); 
    }
}

SynchronizationContext不保证将在async/awate代码的同一线程上执行延续。但是,您可以通过SynchronisationContex关键字使用 TPL 代码设置上下文。

于 2012-09-17T10:33:24.520 回答