C# 在哪个计划程序任务.ContinueWith()上运行?
考虑以下代码:C# 在哪个计划程序任务.ContinueWith()上运行?,c#,task-parallel-library,task,C#,Task Parallel Library,Task,考虑以下代码: // MyKickAssTaskScheduler is a TaskScheduler, IDisposable using (var scheduler = new MyKickAssTaskScheduler()) { Task foo = new Task(() => DoSomething()); foo.ContinueWith((_) => DoSomethingElse()); foo.Start(scheduler);
// MyKickAssTaskScheduler is a TaskScheduler, IDisposable
using (var scheduler = new MyKickAssTaskScheduler())
{
Task foo = new Task(() => DoSomething());
foo.ContinueWith((_) => DoSomethingElse());
foo.Start(scheduler);
foo.Wait();
}
ContinueWith()
任务是否保证在我的计划程序上运行?如果没有,它将使用哪个计划程序
ContinueWith()任务是否保证在我的计划程序上运行?如果没有,,
它将使用哪个调度程序
否,它将使用传递给原始任务的调度程序ContinueWith
将默认使用TaskScheduler.Current
,在这种情况下是默认的线程池任务计划程序。您提供的任务.Start的上下文与continuation中使用的上下文之间没有传播
:
public Task ContinueWith(Action continuationAction)
{
stackcrawmark stackMark=stackcrawmark.LookForMyCaller;
返回ContinueWith(continuationAction,
TaskScheduler.Current,
默认(取消令牌),
TaskContinuationOptions.None,参考stackMark);
}
StartNew,ContinueWith将默认为TaskScheduler。Current,Current将在未从任务(MSDN)中调用时返回默认计划程序
要避免默认计划程序问题,应始终将显式TaskScheduler传递给Task.ContinueWith和Task.Factory.StartNew
@阅读它,但仍然怀疑它的有效性
行为-我在一个非默认调度程序上运行了第一个任务
原因。为什么第三方物流决定继续,这一直是一个问题
按照我的任务顺序,应该在另一个上运行吗
我同意-这不是最好的设计-但我认为默认为TaskScheduler.Current
是为了ContinueWith
与Task.Factory.StartNew
保持一致,后者默认为TaskScheduler.Current
。Stephen Toub后一个设计决策:
在许多情况下,这是正确的行为。比如说
您正在实现一个递归的分而治之问题
有一个任务,它应该处理一些大块的工作,并在
turn细分其工作并安排任务来处理这些块。
如果该任务在代表特定池的计划程序上运行
线程数,或者如果它运行在具有并发性的调度程序上
限制等等,您通常希望它随后创建的任务
也在同一个调度程序上运行
因此,ContinueWith
使用当前(环境)TaskScheduler。调用ContinueWith
时当前正在执行的任何任务的当前
,而不是先前任务的当前
。如果这对您来说是一个问题,并且您无法显式指定任务调度器,那么有一个解决方法。您可以将自定义任务计划程序设置为特定范围的环境任务计划程序,如下所示:
using (var scheduler = new MyKickAssTaskScheduler())
{
Task<Task> outer = new Task(() =>
{
Task foo = new Task(() => DoSomething());
foo.ContinueWith((_) => DoSomethingElse());
foo.Start(); // don't have to specify scheduler here
return foo;
}
outer.RunSynchronously(scheduler);
outer.Unwrap().Wait();
}
使用(var调度程序=新的MyKickAssTaskScheduler())
{
任务外部=新任务(()=>
{
任务foo=新任务(()=>DoSomething());
foo.ContinueWith(()=>DoSomethingElse());
foo.Start();//不必在此处指定调度程序
返回foo;
}
同步运行(调度程序);
外部。展开()。等待();
}
请注意,outer
是Task
,因此有outer.Unwrap()
。您也可以执行outer.Result.Wait()
,但有一些语义差异,特别是如果您使用outer.Start(调度程序)
而不是outer.RunSynchronously(调度程序)的话
我运行了一些测试,发现我的延续程序在不同的计划程序上运行。我推测这是因为TaskScheduler.Current在从任务中调用时返回默认的.Net Framework计划程序。请参阅我的自定义计划程序是的变体,因此使用的STA线程不是来自该线程的pool@bavaza你说得对,我做了一些测试,调度程序没有流动。@YuvalItzchakov,但它是环境任务调度程序:)@YuvalItzchakov,它是运行当前正在执行的任务的任务调度程序。因此,对于在该任务中通过TPL API创建的任何任务,它都会成为环境,您不需要明确指定调度程序(除了task.Run
,它总是使用TaskScheduler.Default
)。请看我答案中的代码。谢谢。这是一个有问题的设计决策还是我遗漏了什么?TPL有人解释过吗?@bavaza-我的新朋友也开始阅读,以获得关于TPL的深入信息it@bavaza,TPL的Stephen Toub在这里提供了一些解释:@Noseratio-阅读它,但仍然怀疑这种行为的有效性-我在非默认调度程序上运行第一个任务是有原因的。为什么TPL决定在另一个任务上运行延续,这总是与我的任务顺序一致?@bavaza,我回答了你的评论。酷把戏:)。尽管如此,我认为我还是会坚持明确指定调度程序,或者完全避免ContinueWith()
。@bavaza,如果您可以完全避免ContinueWith
,而使用wait
,那将是最好的决定。您可以使用自定义的SynchronizationContext
(这将使wait
-友好)围绕线程对调度程序进行建模,只需执行TaskScheduler.FromCurrentSynchronizationContext
。
using (var scheduler = new MyKickAssTaskScheduler())
{
Task<Task> outer = new Task(() =>
{
Task foo = new Task(() => DoSomething());
foo.ContinueWith((_) => DoSomethingElse());
foo.Start(); // don't have to specify scheduler here
return foo;
}
outer.RunSynchronously(scheduler);
outer.Unwrap().Wait();
}