c# TaskFactory 和 TaskScheduler 的关系和自定义

来源:这里教程网 时间:2026-02-21 17:39:53 作者:

TaskFactory 默认用哪个 TaskScheduler?

TaskFactory
本身不执行任务,它只负责创建
Task
实例;真正决定「何时、在哪一线程上运行」的是
TaskScheduler
。默认情况下,
TaskFactory
使用
TaskScheduler.Default
—— 这个调度器背后是 .NET 的线程池(
ThreadPool
),也就是你调用
Task.Run(...)
或无参
new TaskFactory()
时实际走的路径。

关键点:不是

TaskFactory
决定调度,而是它把创建好的
Task
交给某个
TaskScheduler
去排队和执行。你可以显式传入自定义调度器,也可以改写
TaskFactory
Scheduler
属性。

怎么给 TaskFactory 指定自定义 TaskScheduler?

最直接的方式是在构造

TaskFactory
时传入自定义调度器实例:

var myScheduler = new ConcurrentExclusiveSchedulerPair().Scheduler;
var factory = new TaskFactory(myScheduler);

之后所有通过该

factory
创建的任务(如
factory.StartNew(...)
)都会被提交到
myScheduler
执行。注意:
Task.Run(...)
不会受此影响,它始终使用
TaskScheduler.Default

常见错误:以为设置了

TaskScheduler.Default = myScheduler
就能全局生效 —— 这是无效的,
Default
是只读属性,不能赋值。

TaskFactory
Scheduler
属性可读可写,但修改它只影响后续创建的任务,不影响已排队的
若用
Task.Factory
(静态实例),它的
Scheduler
也是可写的,但不建议全局修改,容易引发跨模块冲突
自定义调度器必须继承
TaskScheduler
并实现
QueueTask
GetScheduledTasks
(后者仅调试需要)

自定义 TaskScheduler 最小可行实现长什么样?

一个最简可用的调度器只需把任务立即在当前线程同步执行(用于测试或 UI 线程强制同步场景):

public class SyncTaskScheduler : TaskScheduler, IDisposable
{
    protected override void QueueTask(Task task) => TryExecuteTask(task);
    protected override IEnumerable<Task> GetScheduledTasks() => Enumerable.Empty<Task>();
    protected override void ExecuteTask(Task task) => TryExecuteTask(task);
    public void Dispose() { }
}

这种调度器没有队列、不启新线程,

StartNew
提交的任务会立刻在调用线程上运行。适合单元测试隔离异步行为,或 WinForms/WPF 中确保回调回到 UI 线程(此时应改用
WindowsFormsSynchronizationContext
DispatcherSynchronizationContext
封装)。

性能提示:不要在生产环境用纯同步调度器处理耗时操作,会阻塞调用方线程;真实自定义调度器通常要管理自己的线程/队列(比如限流、优先级、单线程串行等)。

TaskScheduler.UnobservedTaskException 是谁抛的?

这个事件由

TaskScheduler
触发,不是
TaskFactory
。当某个
Task
抛出异常但从未被
await
Wait()
或读取
Exception
属性时,.NET 运行时会在该
Task
被 GC 回收前,通过其关联的
TaskScheduler
触发
UnobservedTaskException

这意味着:如果你用了自定义调度器,且没重写

UnobservedTaskException
的触发逻辑(通常不需要重写),事件仍会按默认机制上报——但上报时机取决于该调度器如何管理任务生命周期。例如,某些自定义调度器延迟释放任务引用,可能导致异常“滞留”更久才被发现。

容易忽略的一点:即使你全程用

TaskFactory
创建任务,只要没处理异常,最终兜底的仍是调度器层面的未观测异常机制,而不是工厂本身。

相关推荐