如何等待嵌套Task执行完成?
解决DoWork任务全部完成后再输出"Hello World!"的问题
这个问题我之前也碰到过,核心是你当前的代码没真正等待DoWork里的三个异步任务完成,就触发了后续的ContinueWith逻辑。咱们一步步来改:
为什么原来的代码会提前输出?
原来的DoWork是void类型,当你用Task.Run(() => DoWork())时,这个外层Task只会等待DoWork方法执行完毕(也就是启动三个HeavyOperation任务的代码跑完),而不会等待那三个后台任务真正完成。所以ContinueWith会立刻触发,输出"Hello World!",但三个Heavy任务还在后台跑。
修改方案
第一步:让DoWork能跟踪子任务的完成状态
把DoWork改成async Task方法,这样它能返回一个Task,用来标记所有子任务的完成状态:
public async Task DoWork() { // 启动三个任务,保存它们的引用 var task1 = Task.Run(() => HeavyOperation()); var task2 = Task.Run(() => HeavyOperation2()); var task3 = Task.Run(() => HeavyOperation3()); // 等待这三个任务全部完成 await Task.WhenAll(task1, task2, task3); }
这里用Task.WhenAll来等待所有子任务完成,只有当三个Heavy任务都结束后,DoWork返回的Task才会标记为完成。
第二步:让外层Task等待DoWork完成
修改Foo方法里的逻辑,现在DoWork返回Task了,外层的Task.Run会等待DoWork的Task完成(也就是所有子任务完成),再触发ContinueWith:
public void Foo() { Task.Run(async () => await DoWork()) .ContinueWith((prev) => { Console.WriteLine("Hello World!"); }); }
或者更简洁一点,因为DoWork本身就是异步方法,直接传进去就行:
public void Foo() { DoWork() .ContinueWith((prev) => { Console.WriteLine("Hello World!"); }); }
更优雅的写法:用await代替ContinueWith
如果允许把Foo改成async方法,用await会比ContinueWith更直观,也能避免一些上下文同步的坑:
public async Task Foo() { await DoWork(); Console.WriteLine("Hello World!"); }
这种写法代码更清晰,可读性更强,推荐在能使用async/await的场景下优先用它。
注意点
- 不要用
async void(除非是事件处理程序),因为async void无法被跟踪,也很难处理异常,这里DoWork用async Task是正确的选择。 Task.WhenAll会等待所有传入的Task完成,不管它们是成功还是失败,如果需要处理异常,可以在await之后或者用try/catch包裹。
内容的提问来源于stack exchange,提问作者utop
相关产品推荐
相关产品推荐

