TaskContinuationOptions是控制延续任务执行时机、调度方式及与前置任务关联的关键枚举,必须显式指定以满足特定场景需求,如异常处理、同步执行、父子任务绑定等;默认None表示异步执行且不关心前置任务状态。

TaskContinuationOptions 用来精确控制「延续任务(continuation)」何时执行、怎么调度、和前置任务如何关联。它不是可有可无的配置项,而是决定你 ContinueWith 行为是否符合预期的关键开关。
什么时候必须指定 TaskContinuationOptions?
当你需要延续任务只在特定状态才运行(比如只处理异常),或要求它同步执行、绑定父任务生命周期、避免线程池争抢时,就必须显式传入。默认不传等价于 TaskContinuationOptions.None —— 它会异步执行、不关心前置任务成败、也不参与父子关系管理。
- 前置任务失败了你还想记录日志?用
TaskContinuationOptions.NotOnRanToCompletion | TaskContinuationOptions.NotOnCanceled - 想让延续逻辑一定在同一线程跑(比如 UI 更新)?加
TaskContinuationOptions.ExecuteSynchronously(但注意:仅当前置任务已完成才生效,否则仍异步) - 写了一个嵌套子任务链,希望父任务
Wait()时等所有子任务结束?必须用TaskContinuationOptions.AttachedToParent
AttachedToParent 和 DenyChildAttach 的真实作用
这两个选项管的是「任务树结构」,不是线程绑定。很多开发者误以为 AttachedToParent 能让子任务和父任务跑在同一个线程上 —— 实际完全无关。它只影响 Task.Status 和 Wait() 行为:
- 父任务设了
AttachedToParent,它的Status不会变成RanToCompletion直到所有附加子任务也完成 - 如果父任务用了
TaskCreationOptions.DenyChildAttach,那么子任务里再写AttachedToParent也会被无视,直接退化为独立任务 - 错误示范:
Task.Run(...).ContinueWith(..., AttachedToParent)—— 这里ContinueWith产生的任务不是「子任务」,AttachedToParent无效(只有new Task(..., AttachedToParent)或Task.Factory.StartNew(..., AttachedToParent)才触发父子关系)
常见踩坑点:位运算、条件组合与多任务延续
TaskContinuationOptions 是带 [Flags] 特性的枚举,支持按位或(|)组合多个行为,但不是所有组合都合理:
-
ExecuteSynchronously | NotOnFaulted合法;但ExecuteSynchronously | OnlyOnFaulted可能导致延续根本没机会执行(因为异常发生时前置任务已非 Running 状态,同步执行条件不满足) -
OnlyOnRanToCompletion等「OnlyOnXxx」系列不能用于ContinueWhenAll或ContinueWhenAny—— 多任务延续不支持这些条件,会抛ArgumentException - 别把
PreferFairness当成「保证顺序」:它只是给TaskScheduler的提示,实际调度仍取决于线程池负载和窃取策略
Task taskA = Task.Run(() => { throw new InvalidOperationException(); });
Task continuation = taskA.ContinueWith(t =>
{
Console.WriteLine("我不会执行,因为用了 OnlyOnRanToCompletion");
}, TaskContinuationOptions.OnlyOnRanToCompletion); // 此延续被跳过
最常被忽略的一点:ContinueWith 默认是「火后不管」型延续 —— 前置任务一完成,延续就进队列,哪怕你没 await、没 Wait、也没保存引用,它照样可能执行(也可能被 GC 中断,取决于是否捕获异常)。要真正稳控流程,得结合 NotOnXxx 条件 + 显式等待 + 异常处理,而不是依赖默认行为。










