C# Task ConfigureAwait
本文关键字:ConfigureAwait Task | 更新日期: 2023-09-27 18:26:09
我以为我已经掌握了ConfigureAwait
,然后我尝试了一个实验。
我的理解是,只有在存在同步上下文的情况下,ConfigureAwait(false)
才会产生影响。
ASP、WPF等应该有上下文,但控制台应用程序和服务应用程序不应该有上下文。
为了了解它的工作原理,我制作了一个Web API应用程序,并包括以下方法:
// GET api/values/5
public async Task<string> Get (int id)
{
var syncCtx = SynchronizationContext.Current;
int startThreadId = Thread.CurrentThread.ManagedThreadId;
await Task.Delay(TimeSpan.FromSeconds(3)).ConfigureAwait(true);
int endThreadId = Thread.CurrentThread.ManagedThreadId;
return "Start Thread ID: " + startThreadId.ToString() +
": End Thread ID: " + endThreadId.ToString();
}
我的预测是,如果没有将ConfigureAwait
或ConfigureAwait
设置为true
,那么在等待之前和之后,我将看到相同的线程ID。
我的前几次测试表明,在上面的真实集合中,这一点是正确的。
无论ConfigureAwait
如何,代码的后续运行都在不同的线程ID上开始和结束。
我添加syncCtx
是为了让自己相信我有上下文。
我读到的一个警告是,如果任务已经完成,你就不能保证拥有相同的ID。这里是这样的吗?如果是,为什么会这样?
我是否设置了一个天真或有缺陷的测试?如果是这样,什么才是合适的测试?
我在控制台/服务应用程序中开始了这条路,并意识到我没有得到相同的线程ID。我按照我见过的大多数"最佳实践"文章中的建议添加了ConfigureAwait(false)
。由于我喜欢了解事情的实际工作方式,所以我尝试测试线程ID。看到它们的不同,我进行了多次搜索,得到了上面的代码。
执行ConfigureAwait(true)
(或者只是关闭它,因为它是默认值)不会使它在同一个线程上运行。它告诉SynchronizationContext.Current
做一个Post
或Send
,剩下的继续。Post或Send实际如何运行该代码尚未定义。
WindowsFormsSynchronizationContext
和DispatcherSynchronizationContext
(WinForms和WPF的上下文)都会将延续放到消息泵(UI线程)要处理的消息队列中。
另一方面,AspNetSynchronizationContext
(这是您正在运行的)只是设置一些状态信息(比如将HttpContext.Current
设置回其旧值),然后将下一个可用线程池线程上的工作排队。ASP.NET没有"消息泵",它只是在线程池上完成所有工作,因此以后尝试从线程池中取出同一个线程是没有意义的,在继续操作时,该线程可能已经被破坏。
你在之前和之后看到相同ID的次数只是运气好,在继续之前和之后,相同的线程被从线程池中拉出。
我强烈建议您阅读MSDN杂志上的文章"It’s All About the SynchronizationContext",它详细解释了SynchronizaContext是如何工作的,并详细介绍了.NET中内置的4种类型的上下文。
SynchronizationContext
!=CCD_ 18。如果您在其Dispatcher
运行时使用类似WPF的东西,那么,是的,其SynchronizationContext
恰好绑定到一个特定线程。然而,像ASP.NET和WCF这样的东西并不是线程仿射的,它们只是附带了特定的环境上下文,这些上下文需要在下一个开始执行的线程上恢复。它们可以仿射到特定线程池,但同样,不是特定线程。
这正是引入SynchronizationContext
抽象的原因:它允许不同的框架准确地决定亲和性对它们意味着什么,并允许异步/等待基础设施以完全不可知的方式在它们之上工作。