Task.Run内async/await的有无会如何影响相关异步任务?
Task.Run委托里用不用async/await,差别到底在哪?
先看你贴的这段代码:
public async Task SomethingAsync() { await SomeAsyncOperation(); } public async Task Execute(int numOfTimes) { var tasks = new List<Task>(); for (var i = 0; i < numOfTimes; i++) { // 写法1:带async/await var task = Task.Run(async () => await SomethingAsync()); // 写法2:不带async/await // var task = Task.Run(() => SomethingAsync()); tasks.Add(task); } await Task.WhenAll(tasks); } public async Task Test() { await Execute(10); }
针对你的场景(用Task.WhenAll等待,没碰.Result),我来拆解两种写法的差异:
核心差异:多了一层异步状态机开销
当你用async () => await SomethingAsync()时,编译器会把这个匿名委托转换成一个异步状态机类——这是C#处理async方法的常规操作。这个状态机需要分配内存、维护执行状态,虽然开销很小,但确实是额外的。
而直接写() => SomethingAsync()时,这个委托只是简单地调用SomethingAsync并返回它生成的Task,没有额外的状态机生成,性能上会略优一点(当然,除非你在极端高频场景下跑,否则这点差异几乎感知不到)。
异常处理的细微区别
如果SomethingAsync在同步阶段就抛出异常(比如方法开头的同步代码出错,还没走到await SomeAsyncOperation()那一步),两种写法都会把异常包装到返回的Task中,最终通过Task.WhenAll暴露给调用者,但底层处理路径不一样:
- 带async/await的写法:async方法本身会捕获同步异常,把它包装到自己返回的
Task里。 - 不带的写法:
Task.Run会捕获委托执行时抛出的同步异常,再包装到返回的Task里。
不过对你的代码来说,最终的异常表现是完全一致的——你都会通过await Task.WhenAll收到这个异常,不用纠结这点差异。
最终结论
在你的代码场景下,两种写法的行为完全一致:都能正确等待所有SomethingAsync完成,异常也能正确传播。但不带async/await的写法更简洁,还少了一点不必要的性能开销,更推荐用这种写法。
内容的提问来源于stack exchange,提问作者globetrotter
相关产品推荐
相关产品推荐

