多线程编程总结:二、Thread的那些坑和填坑的线程池
在.Net的多线程编程中,最基础的一个模块类是Thread类,但是我们在实际开发中却应该少用Thread去直接创建线程。原因如下:
一、Thread.Priority
这个属性允许我们去设置线程的优先级,让线程将时间片(Time Slice)优先分配给优先级高的程序,但是请注意可能会导致优先级高的线程活得很好,但是优先级低的线程一直处于饥饿状态。
二、Thread.Sleep()
这个方法经常会被滥用与生产环境中,对于这个方法,实际上通过这个方法无法操作系统精确保证线程沉睡多少时间,线程唤醒的时间是非常不确定的。另外我们在实际使用过程中如果想让某个线程沉睡某个指定时间期望另外一个异步线程完成计算更是不可取,因为那个异步线程的工作时间可能和你想的很不一样。此外我们在主线程上使用这个沉睡方法更是会让我们的UI界面卡死。
三、Thread.Abort()
这个方式是试图去销毁线程,且会让CLR引发ThreadAbourException异常。但是异常即使被捕捉了,在以下一些情况下可能会重新抛出异常以杀死线程。
a. 这个方法只承诺尽力杀死线程,不完全保证成功。CLR不会终止正在运行在finally代码和非托管代码的线程,因为这会引发ThreadAbourException异常,破坏CLR本身,所以CLR会推迟引发异常,但是如果在finally代码中存在死循环,就无法结束此线程。
b. 被中止的线程正在做Lock语句保护的代码的时候,Thread.Abort()会中止运行到一半的Lock代码,其实破坏了锁的原子性,也就是锁本身的意义被破坏了。其他线程进入了运行一半的原子锁内部。
c. 线程中止的时候可能会将BCL(基础类库)的数据结构损坏,造成程序崩溃或者后续代码错误。
四、线程池填坑
a. 开发人员不直接分配线程,而是告知线程池给我一个线程去做什么事,等事情做完之后,线程不是中断销毁,而是回到线程池中等下次使用。
b. 线程池中的线程并非无限,而是数量相对合理,所以在线程池中的线程使用完之后,其他任务就需要排队导致延迟。为了解决这个问题,我们就需要通过TPL来解决(Task Parallel Library).