我在理解" continueOnCapturedContext"的内部和外部时遇到了一些麻烦。从.NET v4.6 WebAPI 2的角度来看。
我遇到的问题是在ConfigureAwait(true)和ConfigureAwait(false)之间没有任何区别。
我已经整理了一个示例应用,展示了发生了什么:
public async Task<IHttpActionResult> Get(bool continueOnContext)
{
int beforeRunningExampleThreadId = Thread.CurrentThread.ManagedThreadId;
int runningExampleThreadId = await ExecuteExampleAsync(continueOnContext).ConfigureAwait(continueOnContext);
int afterRunningExampleThreadId = Thread.CurrentThread.ManagedThreadId;
return Ok(new
{
HasSyncContext = SynchronizationContext.Current != null,
ContinueOnCapturedContext = continueOnContext,
BeforeRunningExampleThreadId = beforeRunningExampleThreadId,
RunningExampleThreadId = runningExampleThreadId,
AfterRunningExampleThreadId = afterRunningExampleThreadId,
ResultingCulture = Thread.CurrentThread.CurrentCulture,
SameThreadRunningAndAfter = runningExampleThreadId == afterRunningExampleThreadId
});
}
private async Task<int> ExecuteExampleAsync(bool continueOnContext)
{
return await Task.Delay(TimeSpan.FromMilliseconds(10)).ContinueWith((task) => Thread.CurrentThread.ManagedThreadId).ConfigureAwait(continueOnContext);
}
对于&#34; /测试?continueOnContext = true&#34;,这会让我回复:
{"HasSyncContext":true,"ContinueOnCapturedContext":true,"BeforeRunningExampleThreadId":43,"RunningExampleThreadId":31,"AfterRunningExampleThreadId":56,"ResultingCulture":"fr-CA","SameThreadRunningAndAfter":false}
所以你可以看到我有一个Sync上下文,我正在进行ConfigureAwait(true)但是线程还没有&#34; t&#34;继续&#34;以任何方式 - 在运行异步代码之前,运行之后分配新线程。这不是我期望的方式 - 我在这里有一些根本的误解吗?
有人可以向我解释为什么在这段代码中,ConfigureAwait(true)和ConfigureAwait(false)实际上是在做同样的事情吗?
更新 - 我想出来并在下面回答。我也喜欢这个答案 @YuvalShap。如果您像我一样坚持这一点,我建议您同时阅读。
答案 0 :(得分:3)
当异步处理程序恢复对旧版ASP.NET的执行时, continuation排队到请求上下文。必须继续 等待已经排队的任何其他延续(仅限 一个人可以一次跑。准备好运行时,会占用一个线程 从线程池中,输入请求上下文,然后继续 执行处理程序。 “重新进入”请求上下文涉及到 一些内务处理任务,比如设置HttpContext.Current 和当前线程的身份和文化。
来自ASP.NET Core SynchronizationContext Stephen Cleary的博客文章。
总而言之,Core之前的ASP.NET版本使用AspNetSynchronizationContext
作为请求上下文,这意味着当您调用ConfigureAwait(true)
(或不调用ConfigureAwait(false)
)时,您捕获了告诉方法在请求上下文上恢复执行的上下文。
请求上下文保持HttpContext.Current和当前线程的标识和文化一致,但它不是特定线程的唯一限制,唯一的限制是一次只能在一个上下文中运行一个线程。
答案 1 :(得分:2)
好的我明白了,所以我会发一个答案,以防它帮助别人。
在.NET 4.6 WebAPI 2中 - &#34;捕获的上下文&#34;我们继续不是线程,它是请求上下文。除其他外,Request Context知道HttpContext。当指定了ConfigureAwait(true)时,我们告诉.NET我们想要在等待之后保留请求上下文及其所有内容(HttpContext和其他一些属性) - 我们想要返回到我们开始的上下文与 - 这没有考虑到线程。
当我们指定ConfigureAwait(false)时,我们说我们不需要返回我们开始的请求上下文。这意味着.NET可以返回而不必关心HttpContext&amp;一些其他属性,因此边际性能增益。
鉴于知识我改变了我的代码:
public async Task<IHttpActionResult> Get(bool continueOnContext)
{
var beforeRunningValue = HttpContext.Current != null;
var whileRunningValue = await ExecuteExampleAsync(continueOnContext).ConfigureAwait(continueOnContext);
var afterRunningValue = HttpContext.Current != null;
return Ok(new
{
ContinueOnCapturedContext = continueOnContext,
BeforeRunningValue = beforeRunningValue,
WhileRunningValue = whileRunningValue,
AfterRunningValue = afterRunningValue,
SameBeforeAndAfter = beforeRunningValue == afterRunningValue
});
}
private async Task<bool> ExecuteExampleAsync(bool continueOnContext)
{
return await Task.Delay(TimeSpan.FromMilliseconds(10)).ContinueWith((task) =>
{
var hasHttpContext = HttpContext.Current != null;
return hasHttpContext;
}).ConfigureAwait(continueOnContext);
}
当continueOnContext = true时:
{"ContinueOnCapturedContext":true,"BeforeRunningValue":true,"WhileRunningValue":false,"AfterRunningValue":true,"SameBeforeAndAfter":true}
当continueOnContext = false时:
{"ContinueOnCapturedContext":false,"BeforeRunningValue":true,"WhileRunningValue":false,"AfterRunningValue":false,"SameBeforeAndAfter":false}
因此,在此示例中,您可以看到HttpContext.Current在异步方法之前存在,并且在异步方法期间丢失,而不管ConfigureAwait设置如何。
异步操作完成后,差异就出现了: