我对这个话题很困惑。我来自这样一个假设,即任务创建及其调度应该严格分开,在C#中并非如此。
考虑下面的简单代码。
static async Task simpleton()
{
print("simpleton");
}
static async Task cont()
{
print("cont");
}
static void Main(string[] args)
{
Task t1 = simpleton();
Task t2 = t1.ContinueWith((a) => { cont(); });
Thread.Sleep(1000);
return;
}
输出是
simpleton
cont
simpleton函数运行并创建任务t1(已完成)-没关系。但是,t2接缝(至少在代码中)仅是任务创建-系统未要求进行调度,那么为什么以及由谁来调度继续运行?显然,创建/运行原理在这里被打破了。
类似的情况正在等待。在这篇非常著名的文章中考虑伪代码。根据伪代码,wait被转换为继续任务并返回给调用者,我希望后者必须安排任务以完成任务。同样不是这种情况,请考虑以下代码:
static async Task foo()
{
await bar();
}
static async Task bar()
{
print("bar");
}
static void Main(string[] args)
{
foo();
Thread.Sleep(1000);
return;
}
bar的执行无需专门调度由foo创建的Task对象。
所以问题是:
是正确的,ContinueWith不仅创建任务而且安排任务。
正确的是,等待不仅创建了文章中显示的继续任务,而且还尽可能地安排了时间表(在SynchronizationContext或TaskScheduler上发帖)。
为什么异步/等待语言设计师会采用这种设计(计划和创作混合)?
您可以指定TaskScheduler
用于的某些重载ContinueWith
。您决定在哪里运行该代码。不能在此处指定调度程序是不正确的。
异步方法在捕获的对象SynchronizationContext
或TaskScheduler
第一个等待点之后的当前对象上运行。因此,确实,异步方法可以调度继续。(我忽略了您可以有自定义等候者的事实。)
您的异步示例在主线程上同步运行以完成操作。
继续创建不仅可以创建任务,还可以对其进行调度是否正确?
是的,在您指定的调度程序上。
正确的是,等待不仅创建了文章中显示的继续任务,而且还尽可能地安排了时间表(在SynchronizationContext或TaskScheduler上发帖)。
是的,在第一次等待(不会立即完成)之后,将执行调度操作。
为什么异步/等待语言设计师会采用这种设计(计划和创作混合)?
异步/等待的内部非常复杂。引擎盖下有很多机械和非平凡的行为。异步方法的代码实际运行在哪个线程上尤其令人惊讶且不一致。显然,此功能的设计人员很难在所有重要情况下立即使用此功能。这每天都会在堆栈溢出中引发许多有关边缘情况和非常令人惊讶的行为的问题。
您可以使用很少使用的Task.Start (TaskScheduler)
方法来取消创建和计划的时间。
对于继续,此模型无法解决。前提完成时,必须激活继续。如果没有TaskScheduler
这样做,则延续无法运行。这就是为什么在设置延续时必须指定调度程序的原因。
对于异步方法,您还可以通过使用自定义等待程序来取消创建和调度。或使用更简单的模型,例如await Task.Factory.StartNew(..., myScheduler)
。
bar的执行无需专门调度由foo创建的Task对象。
此任务不是基于CPU的任务。这是从来没有计划。这是的支持任务TaskCompletionSource
。在这里指定调度程序没有任何意义。
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句