Task.Run 里传 Action
和 Func<task></task>
的行为完全不同
传
Action是让线程池执行一个「不返回任务的普通方法」,而传
Func<task></task>是让线程池执行一个「返回未启动任务的工厂函数」——但这个返回的
Task本身不会被自动 await 或启动,容易造成悬空任务或意外同步执行。
为什么 Func<task></task>
容易出问题
常见错误是误以为
Task.Run(() => SomeAsyncMethod())会正确调度异步操作。实际上:
SomeAsyncMethod()在线程池线程上被**立即调用**,返回一个
Task
Task.Run只负责运行这个委托,**不关心它返回什么**,也不 await 它 如果
SomeAsyncMethod()内部很快完成(比如直接 return Task.CompletedTask),那整个调用看起来“同步结束”,但真正耗时的 await 部分仍在线程池线程上发生,可能阻塞该线程 更危险的是:若
SomeAsyncMethod()抛异常,异常会包装进返回的
Task,但这个
Task没被 await,就变成未观察的异常,.NET 6+ 默认会终止进程
正确写法:用 Func<task></task>
前必须 await
返回值
如果真要传
Func<task></task>,必须确保外层代码会 await 它的结果,否则不如不用
Task.Run。典型安全用法只有两种: 想把「已存在的、未 await 的
Task」丢到线程池等它——但极少需要,因为
Task本身已可 await 配合
Unwrap()处理嵌套任务:
var outer = Task.Run(() => DoAsyncWork()); // 返回 Task<Task> await outer.Unwrap(); // 等内层 Task 完成
绝大多数场景下,应该直接用
Func<task></task>的等价替代:
await Task.Run(() => { /* 同步计算 */ }); // 正确:CPU 密集型工作
await SomeAsyncMethod(); // 正确:本就是异步,无需 Task.Run 包裹
一句话判断该用哪个
只在需要将「纯同步、CPU 密集」代码卸载到线程池时用
Action;
Func<task></task>几乎总是错的起点——如果你手头有个
async方法,别把它塞进
Task.Run,直接 await 它。真正需要
Func<task></task>的场合,往往说明你已经在处理多层异步封装,这时更要小心任务生命周期和异常传播。
