工作中遇到的一点问题,就是关于线程间的死锁。这种问题还真是不好解决,主要是不好找,自己写的代码还好,如果是别人写的,逻辑还没完全掌握,那可真是个苦活。话说大学那会儿,计算机系统这门课没怎么好好学过,不过隐约还记得线程死锁有四个必要条件,只要打破其中一个条件,就会解决死锁问题。下面先复习一下吧:
(1)
互斥条件:
一个资源每次只能被一个进程使用。
(2) 请求与保持条件: 一个进程因请求资源而阻塞时,对已获得的资源保持不放。
(3) 不剥夺条件: 进程已获得的资源,在末使用完之前,不能强行剥夺。
(4) 循环等待条件: 若干进程之间形成一种头尾相接的循环等待资源关系。
(2) 请求与保持条件: 一个进程因请求资源而阻塞时,对已获得的资源保持不放。
(3) 不剥夺条件: 进程已获得的资源,在末使用完之前,不能强行剥夺。
(4) 循环等待条件: 若干进程之间形成一种头尾相接的循环等待资源关系。
先说说我自己的问题,在程序中有多个需要访问的资源,而这些资源又有多个线程进行访问。如果都是读的动作,那就没有必要加锁之类的了,但是偏偏有些动作涉及了写的动作,这就必须对这些资源加锁了。
这些资源实际上就是一些STL中的Vector 、Map之类的东西,在这之前找了找相关的STL在多线程下的读写安全的讨论。不过都不太符合我自己的实际工作环境,所以只能着手在如何解决这个问题上来了。
我这里出现的问题应该归类为循环等待条件,如下所示:
Thread A Thread B
| |
|
|
Lock A (Read or Write) Lock B(Read or Write)
|
|
........
.....
|
|
Lock B(Read or Write) Lock A(Read or Write)
|
|
|
|
Unlock B
Unlock A
|
|
|
|
Unlock A
Unlock B
实际上这算是一个比较典型的死锁样子了,本来这种情况发生的几率比较小,本来我想用鸵鸟算法来解决的(嘿嘿,就是不管它了)。但是后来一想,这种东西还是不要用自己的小命来搏了,于是想想如何解决吧。
解决方法就是打破四个必要条件之一,我的想法比较简单,就是不让Lock产生嵌套的情况。就是说,Lock A 到 Unlock A之间不能存在其它的Lock动作,这样就能避免这种问题的发生了。虽然说着简单,但是这部分需要对整体的逻辑进行改写,真是费了一番脑筋和时间。当然,对于Lock本身也需要设定一个超时和异常处理的动作,这样就更加完美了。
总结:
在多线程和多个资源锁的情况下,尽量避免锁嵌套的情况,这样可以比较好的避免死锁的问题。这点最好在程序设计之初就要考虑到,这样就可以从前期设计时顾虑到这个问题,能是整体程序的逻辑更符合这个样一个设计,代码写起来可能也会更顺畅一些。