发布于2026-07-18 阅读(0)
扫一扫,手机访问
先给个结论:线程异常不捕获,程序直接崩——这可不是“没报错”,而是根本没机会处理。

很多开发者在排查崩溃问题时,常常陷入一个误区:到处加 try-catch 就以为万事大吉。但对于C#的多线程场景,异常处理完全是另一套规则。哪一个线程、哪一类任务,都有自己专属的异常通道,漏掉一个环节,程序就等着崩给你看。
按钮点击、Timer 的 Tick 事件、DataBinding 的更新操作——所有在 UI 线程上发生的同步异常,都不会被你写的 try-catch 捕获到,也不会触发全局的 AppDomain.UnhandledException。它们只认 Application.ThreadException 这一条路。为什么?因为 Windows 消息循环内部的异常处理机制,不允许外部拦截。
关键点在于:
Main() 方法里、Application.Run() 之前就注册好,否则注册了也白搭e.Exception)、向用户提示、以及决定是否要退出程序(Application.Exit())Task.Run 或新开的 Thread 里抛出的异常,它一概不管显式创建的 new Thread 或者未 await 的 Task.Run,一旦抛出未捕获的异常,就会触发 AppDomain.UnhandledException。但要注意,这个事件有一个“不可撤销”的特点:程序会在事件处理完以后立即终止,没有商量余地。
容易踩坑的地方:
try-catch 包住线程入口方法来“兜底”——异常发生在子线程,主线程根本 catch 不到Main() 开头,而且必须早于任何线程启动TaskScheduler.UnobservedTaskException,配合 Task.Wait() 或 await 来显式观察异常这可能是最容易忽略的地方。async Task 方法内部 throw 不会立刻导致程序崩溃,而是让返回的 Task 进入 Faulted 状态。如果你不 await,这个异常就“沉到底”了,后续完全无感知。
错误写法:DoWorkAsync(); —— 异常被吞掉,无声无息。
正确写法:await DoWorkAsync();,然后外层用 try-catch 才能捕获到。
如果实在需要 fire-and-forget 的场景(比如日志上报),至少要做到:加 .ConfigureAwait(false),并且确保 Task 被 Wait() 或检查 task.Exception。
还有一个绝对要避免的:async void(除了事件处理器的特殊情况)。async void 的异常会直奔 AppDomain.UnhandledException,连拦截的机会都没有。
跨 AppDomain、WCF、旧版 Remoting 这些场景下,异常对象需要被序列化传递。如果自定义异常只写了 public class MyException : Exception,运行时反序列化就会静默失败,或者直接抛 SerializationException。
正确的做法:
MyException()、MyException(string message)、MyException(string message, Exception innerException)[Serializable] 特性,尤其是在 .NET Framework 项目中FileStream、委托、IDisposable 实例,这些会让序列化过程直接翻车说到底,异常处理不是“写个 catch 就完事”那么简单。UI 线程、后台线程、async 线程各自有独立的异常通道,漏掉任何一个环节,程序就在那里等着崩给你看。这才是真正的“硬功夫”。
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
正版软件
正版软件
正版软件
正版软件
正版软件
1
2
3
7
8