请考虑以下代码:
// MyKickAssTaskScheduler is a TaskScheduler, IDisposable
using (var scheduler = new MyKickAssTaskScheduler())
{
Task foo = new Task(() => DoSomething());
foo.ContinueWith((_) => DoSomethingElse());
foo.Start(scheduler);
foo.Wait();
}
ContinueWith()
任务是否可以保证在我的调度程序上运行?如果没有,它将使用哪个调度程序?
答案 0 :(得分:6)
StartNew,ContinueWith将默认为TaskScheduler.Current,当未在任务(MSDN)内调用时,Current将返回Default调度程序。
为避免出现默认调度程序问题,您应始终将显式TaskScheduler传递给Task.ContinueWith和Task.Factory.StartNew。
答案 1 :(得分:1)
ContinueWith()任务是否保证在我的调度程序上运行?如果不, 它将使用哪个调度程序?
不,它将使用传递给原始Task
的调度程序。 ContinueWith
将默认使用TaskScheduler.Current
,在这种情况下是默认的线程池任务调度程序。您提供的上下文与task.Start
之间没有传播,而继续
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
的默认值映射为ContinueWith
与Task.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();
}
请注意,outer
为Task<Task>
,因此outer.Unwrap()
。你也可以outer.Result.Wait()
,但是有一些语义差异,特别是如果你使用outer.Start(scheduler)
而不是outer.RunSynchronously(scheduler)
。