Task.Run 适合什么场景
它适合把 CPU 密集型工作(比如大量计算、图像处理、JSON 解析)从主线程移出去,避免阻塞 UI 或响应。不是所有异步都该用
Task.Run——比如 HTTP 请求、文件读写,应优先用原生
async方法(如
HttpClient.GetAsync、
File.ReadAllTextAsync),它们底层不占线程。 用
Task.Run(() => ComputeHeavyWork())包裹纯计算逻辑,别包
await表达式 不要在
Task.Run里调用另一个
async方法却不
await,否则会得到一个未等待的
Task<task></task>ASP.NET Core 中过度使用
Task.Run可能增加线程池争用,反而降低吞吐量
await Task.WhenAll 和 await Task.WhenAny 的区别
Task.WhenAll等待所有任务完成才继续;
Task.WhenAny一有任一任务完成(成功/异常/取消)就返回,常用于超时控制或竞速请求。
var tasks = new[] {
DownloadAsync("url1"),
DownloadAsync("url2"),
DownloadAsync("url3")
};
// 等全部完成
var results = await Task.WhenAll(tasks);
// 只等第一个完成(比如取最快响应)
var first = await Task.WhenAny(tasks);
var result = await first;
Task.WhenAll抛出异常时,会聚合所有子任务的异常(
AggregateException),需遍历
InnerExceptions
Task.WhenAny返回的是
Task<task></task>,必须再
await才能得到结果值 两者都不改变原任务的执行状态,只是观察行为
忘记 await Task 导致的“火球”问题
声明了
async方法却没
await其返回的
Task,编译器不报错,但任务可能被丢弃、异常静默丢失、资源未释放——这就是常说的“fire-and-forget”陷阱。 除非明确设计为后台任务(且已加异常捕获和日志),否则不要直接调用
DoSomethingAsync()而不
await若真要忽略结果,至少用
_ = DoSomethingAsync();显式表明意图,并确保内部有
try/catch在 ASP.NET Core 控制器中直接丢弃
Task,可能导致请求提前返回而后台任务仍在跑,引发状态不一致
Task.Delay 不是 Thread.Sleep 的异步替代品
Task.Delay是非阻塞的计时器,不占用线程;
Thread.Sleep是同步阻塞,会卡死当前线程。在 async 方法中误用
Thread.Sleep,等于把异步代码写成了伪异步。
public async Task DoWorkAsync()
{
// ✅ 正确:不占线程,可被调度器挂起
await Task.Delay(1000);
// ❌ 错误:同步阻塞,整个 async 流程卡住 1 秒
Thread.Sleep(1000);
}
Task.Delay支持
CancellationToken,可用于实现可取消的等待 它底层基于
System.Threading.Timer,开销远低于新建线程或轮询 在 Unity 或某些受限环境里,
Task.Delay可能不可用,需改用协程或平台特定 API 真正难的不是调用
await,而是判断哪个操作该用原生异步、哪个该扔给线程池、哪个根本不能丢弃——这些边界往往藏在业务逻辑深处,而不是语法里。
