前台/后台线程默认情况下,显式创建的线程都是前台线程(foreground threads) 。只要有一个前台线程在运行,程序就可以保持存活不结束 。当一个程序中所有前台线程停止运行时,仍在运行的所有后台线程会被强制终止 。
这里说的 显示创建,指的是通过new Thread()创建的线程当进程以强制终止这种方式结束时,后台线程执行栈中所有finally块就会被避开 。如果程序依赖finally(或是using)块来执行清理工作,例如释放数据库/网络连接或是删除临时文件,就可能会产生问题 。为了避免这种问题,在退出程序时可以显式的等待这些后台线程结束 。有两种方法可以实现:
非默认情况,指的是将Thread的IsBackground属性设置为true
static void Main (string[] args){ Thread worker = new Thread ( () => Console.ReadLine() ); if (args.Length > 0) worker.IsBackground = true; worker.Start();}
- 如果是显式创建的线程,在线程上调用Join阻塞 。
- 如果是使用线程池线程,使用信号构造,如事件等待句柄 。
线程的 前台/后台状态 与它的 优先级/执行时间的分配无关 。异常处理当线程开始运行后,其内部发生的异常不会抛到外面,更不会被外面的try-catch-finally块捕获到 。
void 异常捕获(){try{new Thread(Go).Start();// 启动t线程,执行Go方法}catch (Exception e){_testOutputHelper.WriteLine(e.Message);}}void Go() => throw null!;// 抛出空指针异常
解决方案是将异常处理移到Go方法中:自己的异常,自己解决static void Go(){try{// ...throw null;// 异常会在下面被捕获// ...}catch (Exception ex){// 一般会记录异常,或通知其它线程我们遇到问题了// ...}}
AppDomain.CurrentDomain.UnhandledException 会对所有未处理的异常触发,因此它可以用于集中记录线程发生的异常,但是它不能阻止程序退出 。void UnhandledException(){AppDomain.CurrentDomain.UnhandledException += HandleUnHandledException;new Thread(Go).Start();// 启动t线程,执行Go方法}void HandleUnHandledException(object sender, UnhandledExceptionEventArgs eventArgs){_testOutputHelper.WriteLine("我发现异常了");}
并非所有线程上的异常都需要处理,以下情况,.NET Framework 会为你处理:- 异步委托(APM)
- BackgroundWorker(EAP)
- 任务并行库(TPL)
有两个方式可以实现强行结束:中断、中止
中断(Interrupt)在一个阻塞线程上调用
Thread.Interrupt
会强制释放它,并抛出ThreadInterruptedException
异常,与上文的一样,这个异常同样不会抛出var t = new Thread(delegate(){try{Thread.Sleep(Timeout.Infinite);// 无期限休眠}catch (ThreadInterruptedException){_testOutputHelper.WriteLine("收到中断信号");}_testOutputHelper.WriteLine("溜溜球~");});t.Start();Thread.Sleep(3000);// 睡3s后中断线程tt.Interrupt();
如果在非阻塞线程上调用Thread.Interrupt
,线程会继续执行直到下次被阻塞时,抛出ThreadInterruptedException
。这避免了以下这样的代码:if ((worker.ThreadState & ThreadState.WaitSleepJoin) > 0)// 线程不安全的{worker.Interrupt();}
??随意中断一个线程是极度危险的,这可能导致调用栈上的任意方法(框架、第三方包)收到意外的中断,而不仅仅是你自己的代码!只要调用栈上发生阻塞(因为使用同步构造),中断就会发生在这,如果在设计时没有考虑中断(在finally块中执行适当清理),线程中的对象就可能成为一个奇怪状态(不可用或未完全释放) 。
经验总结扩展阅读
- go GMP
- 豆腐在冷藏室可以放几天
- 200ml牛奶是多少克
- 一碗米饭的热量是多少
- 45岁男士体脂率多少正常 45岁男人体脂率多少
- 70多岁还能骑自行车吗 70岁老人能骑自行车吗
- 虾干热量有多少 虾干的热量是多少
- 一个水煮蛋多少大卡
- 板栗放冰箱里能存放多久
- 螃蟹干放着能活多久