WinForm升级BackgroundWorker时,Task.Run执行异步lambda有什么好的替代方案?
问题解答
核心方案:无需用Task.Run包裹异步方法,也无需将异步方法改为同步实现,你的并行需求可以通过原生async/await特性直接满足
异步方法的执行逻辑是调用即启动,并不是等到await的时候才开始运行,你要实现两个任务并行执行,直接按如下写法调整即可:
private async void FormEventHandler(object sender, EventArgs e) { // 先后调用两个异步方法,此时两个任务已经启动并行运行 var task1 = AwaitableLongRunningTask_1(); var task2 = AwaitableLongRunningTask_2(); // 等待所有任务执行完成 await Task.WhenAll(task1, task2); // 如果需要获取任务返回值,直接取task1.Result、task2.Result即可,此时任务已完成不会阻塞UI线程 }
针对你顾虑的几个问题的说明
Task.Run的使用场景
如果你的AwaitableLongRunningTask方法内部没有大量CPU密集的前置同步逻辑,那么用Task.Run包裹异步lambda完全是冗余操作。只有当异步方法开头的同步计算逻辑会占用UI线程超过几十毫秒时,才需要用Task.Run把整个方法调度到线程池执行,纯IO绑定的异步方法直接调用就不会阻塞UI。- 异步方法返回值要求
AwaitableLongRunningTask_1和AwaitableLongRunningTask_2绝对不能声明为async void,必须返回Task(无返回值)或者Task<T>(有返回值)。async void只能用在顶层的事件处理方法(比如你这个FormEventHandler就符合要求),普通异步方法用async void会导致方法内抛出的异常无法被正常捕获,直接触发进程崩溃。
额外优化建议
- 任务执行完成后如果需要更新UI控件,直接在
await Task.WhenAll之后的代码操作即可,WinForm的默认同步上下文会自动把后续代码调度回UI线程,不需要手动调用Invoke。 - 如果需要实现中途取消功能,可以给两个异步方法加上
CancellationToken参数,配合CancellationTokenSource使用,即可替代原BackgroundWorker的取消逻辑。 - 异常处理直接把
await Task.WhenAll包裹在try-catch块中,就能捕获所有任务抛出的异常,处理逻辑比BackgroundWorker更简洁。
内容的提问来源于stack exchange,提问作者ccalboni
相关产品推荐
相关产品推荐

