的任务.ContinueWith回调线程
本文关键字:线程 回调 ContinueWith 任务 | 更新日期: 2023-09-27 18:17:06
我试图找到一个答案,但不能。我想知道的是,在哪个线程Task.ContinueWith
委托被调用。对于await,我知道它试图在捕获的SynchronizationContext
上运行它,但ContinueWith
没有任何文档记录。
我也尝试了一个示例程序,虽然它似乎是在Threadpool
线程上调用的,但我怀疑在某些情况下它可能会调用SynchronizationContext
。也许有人能给出一个明确的答案。
这取决于与延续相关联的调度程序。默认情况下,任务延续是通过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