0

0

c# async void 和 async Task 的区别 为什么说要避免async void

畫卷琴夢

畫卷琴夢

发布时间:2026-01-11 01:17:31

|

480人浏览过

|

来源于php中文网

原创

async void 方法不可被 await 且异常无法捕获,仅限 UI 事件处理器使用;其他场景必须改用 async Task 并正确 await,否则将导致控制权丢失、UI 错乱或应用崩溃。

c# async void 和 async task 的区别 为什么说要避免async void

async void 方法无法被 await,调用方彻底失去控制权

你写 async void OnClick(),别人在别处想 await OnClick()?编译器直接报错:Cannot await 'void'。这不是语法糖问题,是语言设计上就切断了等待链路。这意味着:

  • 调用方无法知道这个方法什么时候结束
  • 无法用 .Wait().GetAwaiter().GetResult() 等方式同步等待(会死锁或抛异常)
  • 单元测试中几乎无法断言“它执行完了”或“它按顺序执行了后续逻辑”

比如你在 Blazor 中绑定按钮事件,返回 void,组件会在点击后立刻刷新 UI;而返回 Task,Blazor 会等异步操作完成再调用 StateHasChanged() —— 这个差异常导致 UI 状态错乱。

async void 的异常会直接炸穿线程上下文

这是最危险的一点:async void 里未捕获的异常,不会进入 Task 的异常容器,而是直接扔到当前 SynchronizationContext(比如 UI 线程或 ASP.NET Core 的请求上下文),如果没全局处理,应用可能直接崩溃。

对比:

async Task BadTask() => throw new InvalidOperationException("Boom!");  
// 调用方可捕获:try { await BadTask(); } catch (Exception e) { ... }

async void BadVoid() => throw new InvalidOperationException("Boom!");  
// 调用方 try-catch 完全无效,异常飞走,进程可能终止

尤其在 ASP.NET Core 中,async void 导致的未处理异常会让整个请求无声失败,日志里只有一行 Unhandled exception,排查成本极高。

什么情况下真的只能用 async void?

仅限于 **UI 事件处理器**,且必须满足两个条件:

神卷标书
神卷标书

神卷标书,专注于AI智能标书制作、管理与咨询服务,提供高效、专业的招投标解决方案。支持一站式标书生成、模板下载,助力企业轻松投标,提升中标率。

下载
  • 签名由框架强制要求(如 WinForms 的 Button.Click += (s,e) => { }、WPF 的 RoutedEventHandler、Blazor 的 @onclick
  • 该委托签名明确要求返回 void,你无法改写(例如不能把 EventHandler 改成 Func

其他所有情况都应避免:

  • ❌ 不要在服务层、仓储层、工具类里写 async void
  • ❌ 不要在 Timer.Elapsed 回调里用(应改用 Task.Run + async Task 包装)
  • ❌ 不要在 Array.ForEach 或 LINQ 的 lambda 里写 async void(会静默丢失异常)

正确替换 async void 的实操姿势

当你发现一个 async void 方法本不该存在,改成 async Task 后,往往要连带改三处:

  • 方法签名:从 async void DoWork()async Task DoWork()
  • 调用点:原 DoWork();await DoWork();(注意调用方法本身也得是 async
  • 事件绑定(Blazor/WPF/WinForms):若框架支持,优先用 @onclick="async () => await DoWork()" 这种包装写法

如果旧事件签名强制 void(比如 WinForms),至少在内部加 try/catch 并记录日志,别让它裸奔:

private async void button1_Click(object sender, EventArgs e)
{
    try
    {
        await DoWorkAsync();
    }
    catch (Exception ex)
    {
        MessageBox.Show($"操作失败:{ex.Message}");
        // 或写入 ILogger
    }
}

真正难的不是写对 async Task,而是意识到:一旦用了 async void,你就主动交出了错误传播路径和执行生命周期的控制权——而这往往是线上事故的第一块松动的螺丝。

相关专题

更多
php中foreach用法
php中foreach用法

本专题整合了php中foreach用法的相关介绍,阅读专题下面的文章了解更多详细教程。

41

2025.12.04

javascriptvoid(o)怎么解决
javascriptvoid(o)怎么解决

javascriptvoid(o)的解决办法:1、检查语法错误;2、确保正确的执行环境;3、检查其他代码的冲突;4、使用事件委托;5、使用其他绑定方式;6、检查外部资源等等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

175

2023.11.23

java中void的含义
java中void的含义

本专题整合了Java中void的相关内容,阅读专题下面的文章了解更多详细内容。

96

2025.11.27

lambda表达式
lambda表达式

Lambda表达式是一种匿名函数的简洁表示方式,它可以在需要函数作为参数的地方使用,并提供了一种更简洁、更灵活的编码方式,其语法为“lambda 参数列表: 表达式”,参数列表是函数的参数,可以包含一个或多个参数,用逗号分隔,表达式是函数的执行体,用于定义函数的具体操作。本专题为大家提供lambda表达式相关的文章、下载、课程内容,供大家免费下载体验。

202

2023.09.15

python lambda函数
python lambda函数

本专题整合了python lambda函数用法详解,阅读专题下面的文章了解更多详细内容。

189

2025.11.08

Python lambda详解
Python lambda详解

本专题整合了Python lambda函数相关教程,阅读下面的文章了解更多详细内容。

41

2026.01.05

线程和进程的区别
线程和进程的区别

线程和进程的区别:线程是进程的一部分,用于实现并发和并行操作,而线程共享进程的资源,通信更方便快捷,切换开销较小。本专题为大家提供线程和进程区别相关的各种文章、以及下载和课程。

479

2023.08.10

c++主流开发框架汇总
c++主流开发框架汇总

本专题整合了c++开发框架推荐,阅读专题下面的文章了解更多详细内容。

25

2026.01.09

c++框架学习教程汇总
c++框架学习教程汇总

本专题整合了c++框架学习教程汇总,阅读专题下面的文章了解更多详细内容。

23

2026.01.09

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Django 教程
Django 教程

共28课时 | 3万人学习

SciPy 教程
SciPy 教程

共10课时 | 1.1万人学习

Sass 教程
Sass 教程

共14课时 | 0.8万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号