Task.ContinueWith回调线程

时间:2015-10-10 06:54:31

标签: c# .net multithreading task-parallel-library task

我试图找到答案,但无法解决。我想知道的是调用哪个线程Task.ContinueWith委托。 等待我知道它尝试在捕获的SynchronizationContext上运行它,但没有记录ContinueWith

我也尝试过一个示例程序,虽然它似乎是在Threadpool线程上调用的,但我怀疑在某些情况下它可能会调用SynchronizationContext。也许有人可以提供明确的答案。

2 个答案:

答案 0 :(得分:3)

这取决于与continuation关联的调度程序。默认情况下,任务延续通过Current调度程序进行调度,即与当前正在执行的任务相关联的TaskScheduler。如果未在任务中调用ContinueWithCurrent将返回Default调度程序,这是.NET Framework提供的默认TaskScheduler实例,并将安排您在线程池上的任务。

如果要影响此行为,可以调用带有ContinueWith参数的TaskScheduler重载之一。一种常见的模式是在UI线程上创建continuation时传递TaskScheduler.FromCurrentSynchronizationContext(),因为这会导致在执行时将continuation调度回UI线程。

编辑:回复your comment:如果您从UI线程上运行的延续中生成子任务(旨在在线程池上运行),则可能会出现死锁。在这种情况下,子任务将从父任务继承任务调度程序,父任务将绑定到UI线程,从而导致子任务也在UI线程上运行。

Task.Factory.StartNew(() =>
{
    // Do background work.
}).ContinueWith(_ =>
{
    // Update UI, then spawn child task to do more background work...
    Task.Factory.StartNew(() =>
    {
        // ...but child task runs on UI thread!
    });
},
    CancellationToken.None,
    TaskContinuationOptions.None,
    TaskScheduler.FromCurrentSynchronizationContext());

要解决此问题,您可以使用StartNew重载接受子任务的TaskScheduler参数,并将TaskScheduler.Default传递给它:

    // Update UI, then spawn child task to do more background work...
    Task.Factory.StartNew(() =>
    {
        // ...and child task now runs on the thread pool.
    },
        CancellationToken.None,
        TaskCreationOptions.None,
        TaskScheduler.Default);

答案 1 :(得分:2)

Task.ContinueWith计划在TaskScheduler.Current,除非其他一个可选重载中的参数另有说明。

如果TaskScheduler.Current中没有自定义计划程序(很有可能),您的续约将在ThreadPool上运行。

Task.ContinueWith从不使用SynchronizationContext,除非您使用TaskScheduler创建TaskScheduler.FromCurrentSynchronizationContext

您始终可以使用其中一个可用重载明确说明需要TaskScheduler

task.ContinueWith(
    _ => {}, 
    null, 
    CancellationToken.None, 
    TaskContinuationOptions.None, 
    TaskScheduler.Default); // Scheduled to the ThreadPool