理解 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
有以下问题:
它保证将在同一线程上执行延续。
但是,是否有任何保证线程的上下文信息是持久的?我的意思是Name
、CurrentPrincipal
、CurrentCulture
、CurrentUICulture
等。它是否依赖于框架(ASP.NET、WinForms、WCF、WPF)?
【问题讨论】:
一般来说,SynchronizationContext
不保证继续会在同一个线程上执行。一些上下文(WPF,Winforms),但其他没有(ASP.NET)。
您可以为线程like this设置默认文化。
【参考方案1】:
async/await 本身与并发/并行无关,只是 CPS 实现,我说对了吗?
嗯,async
/await
是使用CPS的重写,所以你的核心理解是正确的。
关于“并发”和“并行”,我会说它确实可以实现并发;您可以同时启动多个“正在运行”的async
操作。使用Task.WhenAll
和Task.WhenAny
很容易做到这一点。
此外,即使async
本身并不意味着“多线程”,Task.Run
确实可以轻松实现与async
兼容的多线程
真正的线程是由等待传递/恢复的 SynchronizationContext 实例执行的?
这样想:由 CPS 重写创建的延续必须在某处运行。捕获的“异步上下文”可用于安排继续。
旁注:捕获的上下文实际上是SynchronizationContext.Current
除非它为空,在这种情况下捕获的上下文是TaskScheduler.Current
。
另一个重要的注意事项:上下文的捕获和恢复实际上取决于“等待者”对象。因此,默认情况下,如果您 await
和 Task
(或任何其他内置的可等待对象),则会捕获并恢复上下文。但是如果你await
是ConfigureAwait(false)
的结果,那么上下文就不会被捕获。同样,如果您await
您自己的自定义等待对象,它不会捕获上下文(除非您对其进行编程)。
但是,是否有任何保证线程的上下文信息是持久的?我的意思是 Name、CurrentPrincipal、CurrentCulture、CurrentUICulture 等。
SynchronizationContext
与 ExecutionContext
不同。一个简化的答案是 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