c# Task.Run 中的 Action 和 Func 有什么区别

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

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>
的场合,往往说明你已经在处理多层异步封装,这时更要小心任务生命周期和异常传播。

相关推荐