C#如何在一个任务被取消时启动另一个任务
问题解答
C#完全可以实现「指定任务被取消时启动其他任务」的逻辑,你的代码不符合预期是写法存在明显错误,不是框架能力不支持。
原代码的核心问题
- 重复调用了两次
runTask5():第一次调用的Task5实例直接被丢弃,既没有等待执行也没有跟踪状态;第二次调用是全新启动的Task5实例,你在它刚启动的瞬间就去读Status属性,此时任务还在排队/执行阶段,永远不可能拿到Canceled的终态,判断逻辑完全失效。 - 没有等待任务进入终态就做状态判断:Task的
Canceled、Faulted、RanToCompletion都是终态,必须等任务执行完毕才会进入,你在ContinueWith里同步调用完runTask5()就立刻检查状态,此时任务大概率还在运行,根本等不到取消信号。 - 没有区分Task5的不同终态:你没有对Task5正常完成、抛业务异常、被取消三种终态做分支处理,无法精准匹配「仅取消时启动Task4」的要求。
正确实现方式
优先用async/await编写,可读性和出错概率远低于直接用ContinueWith:
// 并行启动Task1、Task2 var task1 = runTask1(); var task2 = runTask2(); // 等待任意一个任务完成后启动Task5 await Task.WhenAny(task1, task2); var task5 = runTask5(); try { await task5; // Task5正常完成、抛出业务异常的场景,不触发Task4 } catch (OperationCanceledException) when (task5.IsCanceled) { // 仅当Task5被取消时,才启动并等待Task4 await runTask4(); }
实现逻辑说明:
- 任务的取消状态本质是通过抛出
OperationCanceledException传递的,直接await任务就能在任务进入终态后拿到准确的状态,不会出现判断时机不对的问题。 - 异常过滤
when (task5.IsCanceled)可以精准匹配取消令牌触发取消的场景,不会把业务代码手动抛出OperationCanceledException的场景误判为任务取消,完全匹配需求。
如果你因为特殊原因必须使用ContinueWith写法,需要注意处理嵌套Task、跟踪同一个Task5实例,参考如下:
Task.WhenAny(runTask1(), runTask2()) .ContinueWith(async _ => { var task5 = runTask5(); try { await task5; } catch (OperationCanceledException) when (task5.IsCanceled) { await runTask4(); } }) .Unwrap(); // 必须调用Unwrap解包嵌套Task,否则外层无法正确跟踪整个任务链的状态
注意事项
- 所有返回
Task的异步方法,调用后不要直接丢弃返回值,要么await等待,要么把实例保存下来跟踪状态,否则既无法捕获异常,也无法拿到准确的执行状态。 - 不要在任务启动后立刻读取
Status判断终态,必须等任务执行完成后再做判断,否则结果完全不可靠。
内容的提问来源于stack exchange,提问作者QuangGiap
相关产品推荐
相关产品推荐

