2014-10-10 48 views
6

想到我正在处理ConfigureAwait,然后我尝试了一个实验。C#任务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(); 
} 

我的预测是,没有ConfigureAwaitConfigureAwait设置为true,我之前看到相同的线程ID和等待之后。

我的前几个测试显示了正确的设置,如上所述。

不管ConfigureAwait是什么,代码的后续运行开始并结束于不同的线程ID。

我加了syncCtx来说服自己我有一个上下文。

我读过的一个警告是,如果任务已完成,则不能保证具有相同的ID。这是这种情况吗?如果是这样,为什么是这样呢?

我是否设置了一个天真或有缺陷的测试?如果是这样,什么才是正确的测试?

我在控制台/服务应用程序中开始了这个路径,并意识到我没有得到相同的线程ID。我在添加ConfigureAwait(false),正如我见过的大多数“最佳实践”写作中所推荐的那样。由于我喜欢看到事情真的起作用,所以我试着测试线程ID。看到他们的不同导致我通过了一些导致上述代码的搜索。

回答

8

ConfigureAwait(true)(或只是将它关闭,因为它是默认值)不会使它在同一个线程上运行。它告诉SynchronizationContext.Current做一个PostSend与其余的继续。 Post或Send如何实际运行该代码未定义。

WindowsFormsSynchronizationContextDispatcherSynchronizationContext(WinForms和WPF的上下文)都将继续处理由消息泵(UI线程)处理的消息队列。

在另一方面,AspNetSynchronizationContext(这是你下运行)只是设置一些状态信息了(比如设置HttpContext.Current回到其旧值),它然后排队的下一个可用线程池线程的工作。没有用于ASP.NET的“消息泵”,它只是完成了它在线程池中的所有工作,因此在稍后尝试从线程池中获取相同的线程没有任何意义,该线程可能已经在继续发生的时候已经被破坏了。

在你刚刚幸运之前和之后你看到相同的ID的次数,并且在继续之前和之后,同一个线程退出了线程池。

我强烈建议您阅读MSDN杂志文章“It's All About the SynchronizationContext”,它详细解释了SynchronizationContext的工作方式,并详细介绍了内置于.NET中的四种上下文类型的细节。

+0

只是为了澄清我不*试图*得到相同的线程,只是为了纠正我对ConfigureAwait行为的误解。 – jeffa00 2014-10-10 23:27:07

+0

这很好,我仍然会阅读MSDN杂志的文章。它很好地涵盖了这些主题。 – 2014-10-11 02:09:13

+0

我绝对打算阅读那篇文章。感谢您的链接。 – jeffa00 2014-10-11 02:20:29

6

SynchronizationContext!= Thread。如果您正在使用WPF,其Dispatcher运行时然后,是的,它的SynchronizationContext恰好绑定到一个特定的线程。然而,像ASP.NET和WCF这样的东西不是线程仿射的,它们只是伴随着它们特定的环境上下文,需要在接下来开始执行的任何线程上恢复。他们可能会仿效特定的线程,但是,不是特定的线程。

这就是为什么SynchronizationContext抽象被引入的原因:它允许不同的框架来确定它们对它们意味着什么,并允许异步/等待基础架构以完全不可知的方式在它们之上工作。