Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/317.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 异步CTP-任务调度的推荐方法_C#_.net_Task Parallel Library_Async Ctp_.net 4.5 - Fatal编程技术网

C# 异步CTP-任务调度的推荐方法

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的方式 我对新方法(隐式调度)的唯一问题是,我们以前的理念一直是“我们知道延续将始终指定他们的任务调度器,因此我们不需要担心我们在什么环境下完成任务” 远离它确实会让我们有些担心,因为它在避免细微的线程错误方面起到了非常好的作用,因为对于每一个代码,我们可以看到编码器已经记住要考虑他所使用的线程。如

我目前正在开发一个主要使用TAP的异步应用程序。每个类都有生成
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)
,那么它将完成