的任务.ContinueWith回调线程

本文关键字:线程 回调 ContinueWith 任务 | 更新日期: 2023-09-27 18:17:06

我试图找到一个答案,但不能。我想知道的是,在哪个线程Task.ContinueWith委托被调用。对于await,我知道它试图在捕获的SynchronizationContext上运行它,但ContinueWith没有任何文档记录。

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

的任务.ContinueWith回调线程

这取决于与延续相关联的调度程序。默认情况下,任务延续是通过Current调度器调度的,即与当前正在执行的任务相关联的TaskScheduler。当ContinueWith不在任务中被调用时,Current将返回Default调度器,这是。net框架提供的默认TaskScheduler实例,它将在线程池上调度任务。

如果你想影响这个行为,你可以调用一个TaskScheduler参数的ContinueWith重载。一个常见的模式是在UI线程上创建延续时传递TaskScheduler.FromCurrentSynchronizationContext(),因为这会导致延续在执行时被分派回UI线程。

Edit:回复你的评论:如果你从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);

Task.ContinueWith被调度到TaskScheduler.Current上,除非在一个可选的重载参数中另有指定。

如果您在TaskScheduler.Current中没有自定义调度程序(这很有可能),您的continuation将在ThreadPool上运行。

Task.ContinueWith从不使用SynchronizationContext,除非你用TaskScheduler.FromCurrentSynchronizationContext创建一个TaskScheduler

您总是可以使用可用的重载之一显式地声明需要哪个TaskScheduler:

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