理解 C# 5 async/await 中的上下文

Posted

技术标签:

【中文标题】理解 C# 5 async/await 中的上下文【英文标题】:Understanding context in C# 5 async/await 【发布时间】:2012-09-17 09:05:19 【问题描述】:

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

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

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

【问题讨论】:

一般来说,SynchronizationContext保证继续会在同一个线程上执行。一些上下文(WPF,Winforms),但其他没有(ASP.NET)。 您可以为线程like this设置默认文化。 【参考方案1】:

async/await 本身与并发/并行无关,只是 CPS 实现,我说对了吗?

嗯,async/await是使用CPS的重写,所以你的核心理解是正确的。

关于“并发”和“并行”,我会说它确实可以实现并发;您可以同时启动多个“正在运行”的async 操作。使用Task.WhenAllTask.WhenAny 很容易做到这一点。

此外,即使async 本身并不意味着“多线程”,Task.Run 确实可以轻松实现与async 兼容的多线程

真正的线程是由等待传递/恢复的 SynchronizationContext 实例执行的?

这样想:由 CPS 重写创建的延续必须在某处运行。捕获的“异步上下文”可用于安排继续。

旁注:捕获的上下文实际上是SynchronizationContext.Current除非它为空,在这种情况下捕获的上下文是TaskScheduler.Current

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

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

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

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


为了进一步阅读,我建议从我自己的async / await tutorial 开始,然后是official async / await FAQ。那就看看Stephen Toub's blog post on ExecutionContext vs. SynchronizationContext吧。

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

【讨论】:

谢谢。 Stephen Toub 关于 ExecutionContext 与 SynchronizationContext 的博文的链接对我非常有帮助。 老兄,你完全异步了。无处不在。不错! Stephen Toub 帖子的链接是 403,但我也在这里找到了它devblogs.microsoft.com/pfxteam/…【参考方案2】:

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

【讨论】:

以上是关于理解 C# 5 async/await 中的上下文的主要内容,如果未能解决你的问题,请参考以下文章

python 3.5 中的 async/await 关键字是不是受到 C# 中的 async/await 的启发? [关闭]

理解 JavaScript 的 async/await

理解 JavaScript 的 async/await

[转] 理解 JavaScript 的 async/await

从async await 报错Unexpected identifier 谈谈对上下文的理解

理解 Node JS 中的 async/await