在哪个调度程序Task.ContinueWith()运行?

时间:2015-06-29 09:17:23

标签: 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);
    foo.Wait();
}

ContinueWith()任务是否可以保证在我的调度程序上运行?如果没有,它将使用哪个调度程序?

3 个答案:

答案 0 :(得分:6)

StartNew,ContinueWith将默认为TaskScheduler.Current,当未在任务(MSDN)内调用时,Current将返回Default调度程序。

为避免出现默认调度程序问题,您应始终将显式TaskScheduler传递给Task.ContinueWith和Task.Factory.StartNew。

ContinueWith is Dangerous

答案 1 :(得分:1)

  

ContinueWith()任务是否保证在我的调度程序上运行?如果不,   它将使用哪个调度程序?

不,它将使用传递给原始Task的调度程序。 ContinueWith将默认使用TaskScheduler.Current,在这种情况下是默认的线程池任务调度程序。您提供的上下文与task.Start之间没有传播,而继续

中使用的上下文之间没有传播

From the source

public Task ContinueWith(Action<Task> continuationAction)
{
    StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
    return ContinueWith(continuationAction, 
                        TaskScheduler.Current, 
                        default(CancellationToken),
                        TaskContinuationOptions.None, ref stackMark);
}

答案 2 :(得分:1)

  <@> @Noseratio - 阅读它,但仍对此有效性持怀疑态度   行为 - 我在一个非默认调度程序上运行第一个任务   原因。为什么TPL决定延续,这总是如此   顺序到我的任务,应该在另一个上运行?

我同意 - 这不是最好的设计 - 但是我将TaskScheduler.Current的默认值映射为ContinueWithTask.Factory.StartNew一致,默认为TaskScheduler.Current首先,一开始。 Stephen Toub does explain后者的设计决定:

  

在许多情况下,这是正确的行为。例如,让我们说   你正在实施一个递归的分而治之的问题   有一个任务应该处理一些工作,并在其中   转动细分其工作并安排任务来处理这些块。   如果该任务在代表特定池的调度程序上运行   线程,或者它是否在具有并发性的调度程序上运行   限制,等等,你通常想要它创建的那些任务   也可以在同一个调度程序上运行。

因此,ContinueWith使用您调用TaskScheduler.Current时当前正在执行的任何任务的当前(环境)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();
}

请注意,outerTask<Task>,因此outer.Unwrap()。你也可以outer.Result.Wait(),但是有一些语义差异,特别是如果你使用outer.Start(scheduler)而不是outer.RunSynchronously(scheduler)