|
前几天被人问道:如果多线程程序出现死锁,怎么办?突然之间,觉得无法回答.在我写过的程序当中,似乎还没有发生过单纯的死锁问题.自己也写了不少多线程程序了,复杂一点的,超过10个以上的线程互相纠缠,为什么一直没有出现死锁呢?一定是我的的某些行为,能够有效的避免死锁,把他们找出来,将是有价值的.于是,我把最近写的一些项目代码翻出来,仔细的检查了一遍.
多线程发生死锁,无非是占着碗里的,看着锅里的,如能避免这一点,自然就不会发生死锁.然而显示世界并非如此简单.还是从最近的项目谈起吧.
第一,最近的项目使用了boost.thread作为线程库,个人认为这时非常好的库,或许不够强大,但是正是这种不够强大,却使得代码更为健壮.结合boost.bind,创建线程变得简单而直接,杜绝了线程异常退出和创建线程过程中可能出现的资源泄漏.通过bind,我们可以方便的运用RAII,而不必费心思考虑资源拥有权的转移问题.唯一需要注意的就是:线程执行单元依赖的对象生存期一定要比线程还长.在前一个项目中我们没有能够实现一种机制来保证这一点,并且,在这个问题上我们确实发生过一次关联到这个问题上的隐蔽的错误.
结论:选择一个可靠的线程库是非常重要的.
第二,对于锁的使用上,整个项目只有一种RAII的使用形式.用RAII来确保锁的成对操作,其重要性无论如何强调都不过分.实际上,我们还采用了单一的锁定语法.仍然是使用boost.thread提供的锁来实现的.
结论:必须通过RAII来操纵锁.
第三,从未直接操纵存在竞争的原始资源,对于竞争的资源,全部提供了非竞争的接口,这是通过一个间接层来实现的.增加了一个访问资源的间接层,尽量避免高层代码控制资源的获取.另外,因为为每个资源类型实现间接层,避免了一个锁控制访问多个资源的情况.对于需要获取多个资源的,在更高一层把这种获取逻辑抽取出来,提供一个统一的资源获取逻辑,而在实现内部,确保有序获取资源,在算法上避免死锁.
结论:为资源的竞争访问提供抽象,并屏蔽细节.
我想,坚持这三点,应该可以避免绝大多数的线程死锁问题了. |
|