C# 异步CTP-任务调度的推荐方法
我目前正在开发一个主要使用TAP的异步应用程序。每个类都有生成C# 异步CTP-任务调度的推荐方法,c#,.net,task-parallel-library,async-ctp,.net-4.5,C#,.net,Task Parallel Library,Async Ctp,.net 4.5,我目前正在开发一个主要使用TAP的异步应用程序。每个类都有生成Tasks的方法,其中也注入了TaskScheduler。这使我们能够执行任务的显式调度,据我所知,这不是Microsoft使用异步CTP的方式 我对新方法(隐式调度)的唯一问题是,我们以前的理念一直是“我们知道延续将始终指定他们的任务调度器,因此我们不需要担心我们在什么环境下完成任务” 远离它确实会让我们有些担心,因为它在避免细微的线程错误方面起到了非常好的作用,因为对于每一个代码,我们可以看到编码器已经记住要考虑他所使用的线程。如
Task
s的方法,其中也注入了TaskScheduler
。这使我们能够执行任务的显式调度,据我所知,这不是Microsoft使用异步CTP的方式
我对新方法(隐式调度)的唯一问题是,我们以前的理念一直是“我们知道延续将始终指定他们的任务调度器,因此我们不需要担心我们在什么环境下完成任务”
远离它确实会让我们有些担心,因为它在避免细微的线程错误方面起到了非常好的作用,因为对于每一个代码,我们可以看到编码器已经记住要考虑他所使用的线程。如果他们没有指定任务调度程序,那就是一个bug
问题1:有人能向我保证隐式方法是个好主意吗?我看到传统/第三方代码中的ConfigureAwait(false)和显式调度引入了很多问题。例如,如何确保我的“等待已久”代码始终在UI线程上运行 问题2:因此,假设我们从代码中删除所有TaskScheduler
DI并开始使用隐式调度,那么我们如何设置默认的任务调度程序?在等待一个昂贵的方法之前,在方法中途更改调度器,然后再将其设置回原来的状态,怎么样
(顺便说一句,我已经读过了)我想试着回答一下 问题1:有人能向我保证隐式方法是个好主意吗?我看到传统/第三方代码中的ConfigureAwait(false)和显式调度引入了很多问题。例如,如何确保我的“等待已久”代码始终在UI线程上运行
ConfigureAwait(false)
的规则非常简单:如果方法的其余部分可以在线程池上运行,请使用它;如果方法的其余部分必须在给定上下文(例如UI上下文)中运行,请不要使用它
一般来说,ConfigureAwait(false)
应该由库代码使用,而不是由UI层代码使用(包括UI类型层,如MVVM中的ViewModels)。如果该方法部分为后台计算,部分为UI更新,则应将其分为两种方法
问题2:那么,假设我们从代码中删除所有TaskScheduler DI并开始使用隐式调度,那么如何设置默认的任务调度程序
async
/await
通常不使用TaskScheduler
;他们使用“调度上下文”概念。这实际上是SynchronizationContext.Current
,只有在没有SynchronizationContext
时才返回到TaskScheduler.Current
。因此,可以使用SynchronizationContext.SetSynchronizationContext
替换您自己的计划程序。您可以在中阅读有关同步上下文的更多信息
默认的调度上下文应该是您几乎所有时间都需要的,这意味着您不需要处理它。我只在进行单元测试或控制台程序/Win32服务时更改它
在等待一个昂贵的方法之前,在方法中途更改调度器,然后再将其设置回原来的状态,怎么样
如果要执行昂贵的操作(可能是在线程池上),则等待TaskEx.Run的结果
如果由于其他原因(例如并发性)要更改计划程序,则等待TaskFactory.StartNew的结果
在这两种情况下,方法(或委托)在另一个调度程序上运行,然后方法的其余部分在其常规上下文中恢复
理想情况下,您希望每个async
方法都存在于单个执行上下文中。如果方法的不同部分需要不同的上下文,那么将它们分成不同的方法。此规则的唯一例外是ConfigureAwait(false)
,该规则允许方法在任意上下文上启动,然后在剩余的执行过程中恢复到线程池上下文ConfigureAwait(false)
应该被认为是一种优化(对于库代码,默认情况下是打开的),而不是一种设计理念
以下是我的“线程已死”演讲中的一些观点,我认为这些观点可能对您的设计有所帮助:
- 遵循基于任务的异步模式指导原则
- 随着代码库变得更加异步,它在本质上将变得更加功能化(与传统的面向对象相反)。这是正常的,应该接受
- 随着代码库变得更加异步,共享内存并发逐渐演变为消息传递并发(即新的
ReaderWriterLock
)
回答得很好,Stephen,但要澄清的是:如果异步方法“A”在内部使用ConfigureAwait(false),那么如果我等待方法“A”,我希望处于哪个上下文中?线程池,或者它会在对方法“A”进行等待的调用之前恢复原始上下文吗?Stephen的答案非常可靠。请注意,如果您对旧模型死心塌地,则始终可以创建一个自定义包装器awaitable(类似于ConfigureAwait()的工作方式),并将其作为任务/任务的扩展方法挂钩。例如,如果您的扩展方法被称为ResumeOn(TaskScheduler ts),那么代码可能如下所示:wait Foo(…).ResumeOn(ts);然后拥有与您自己的代码相同的调度语义,但具有“wait”带来的所有改进的流/执行优点。@Lawrence:异步方法的每个“层”向下传递上下文,而不是向上传递上下文。因此,如果A
调用ConfigureAwait(false)
,那么它将完成