以为我正在处理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。看到它们的不同使我经历了一系列导致上述代码的搜索。
答案 0 :(得分:8)
执行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”,它详细解释了SynchronizationContext的工作原理,并详细介绍了.NET内置的4种类型的上下文。 / p>
答案 1 :(得分:6)
SynchronizationContext
!= Thread
。如果您使用的是WPF及其Dispatcher
运行时,那么,是的,它的 SynchronizationContext
恰好与一个特定的线程相关联。然而,像ASP.NET和WCF这样的东西不是线程仿射,它们只是随身携带特定的环境上下文,需要在接下来开始执行的任何线程上恢复。它们可能与特定线程 pool 相关,但同样不是特定线程。
这正是引入SynchronizationContext
抽象的原因:它允许不同的框架确切地确定亲和力对它们意味着什么,并允许async / await基础设施以完全不可知的方式在它们之上工作。