前不久在工作中遇到一个死锁的问题,特记录下。
假设有这样的一个场景, 你调用某个接口, 这个接口调用的过程中,会上两个读写锁,上锁的顺序是先A后B。然后在底层网络io回调的时候,也会使用到这两个锁,上锁的顺序是先B后A。 那么就存在这样一种情况。假如线程T1调用接口,刚上完A锁,还没有上B锁。底层回调线程T2刚上完B锁,还没有上A锁。这个时候,线程T1要申请上B锁,但是B锁已经被线程T2占用,无法成功,T1线程阻塞,无法及时将A锁释放,线程T2要申请上A锁,但是因为A锁被T1线程占用,这样T2线程也阻塞了,无法释放B锁。这样就出现相互等待对方释放锁资源的问题,从而死锁了。
- // call interface in thread 1
- void callinterface()
- {
- Utility::WriterLocker lockerA(mutexA);
-
- // do something
- doSomething1();
-
- Utility::WriteLocker lockerB(mutexB);
-
- // do something
- doSomething2();
-
- // io service
-
- doIoService();
-
- }
-
-
- // ioservice call back in thread 2
- void callback()
- {
- Utility::WriterLocker lockerB(mutexB);
-
- // do something
- doSomething3();
-
- Utility::WriteLocker lockerA(mutexA);
-
- // do something
- doSomething4();
- }
解决这个问题的方法,可以有两种方法。
一、假如锁A和锁B实在同一个模块,我们可以使用递归锁代替读写锁,递归锁的特性就是,同一个线程可以进同一个锁多次, 不过要注意的是,锁A和锁B要使用相同的递归锁互斥对象
- // user recursive locker
-
- // call interface in thread 1
- void callinterface()
- {
- Utility::RecursiveLocker lockerA(recursive_mutex);
-
- // do something
- doSomething1();
-
- Utility::RecursiveLocker lockerB(recursive_mutex);
-
- // do something
- doSomething2();
-
- // io service
-
- doIoService();
-
- }
-
-
- // ioservice call back in thread 2
- void callback()
- {
- Utility::RecursiveLocker lockerB(recursive_mutex);
-
- // do something
- doSomething3();
-
- Utility::RecursiveLocker lockerA(recursive_mutex);
-
- // do something
- doSomething4();
- }
二、 锁A和锁B在不同的模块,比如锁B是在某个dll中。这个时候可以这样,如下图
调用接口的时候,使用锁A在线程T1,然后使用锁B的过程放在另一个线程T2。回调的时候,使用锁B的时候,在线程T3,使用锁A的时候,在线程T4。这样就不会出现相互等待对方释放锁资源的问题,从而避免了死锁.
- // use locker A in thread 1
- void callinterface()
- {
- // use locker A
- Utility::WriterLocker lockerA(mutexA);
-
- // do something
- doSomething1();
-
- // user locker B in anohter thread
- m_threads.get_io_service().post(boost::bind(doSomething2));
-
- }
-
- // use locker B in thread 2
- void doSomething2()
- {
- Utility::WriteLocker lockerB(mutexB);
-
- // do something
- realDoSomething2();
-
- // io service
-
- doIoService();
- }
-
-
- // ioservice call back in thread 3, user locker B
- void callback()
- {
- Utility::WriterLocker lockerB(mutexB);
-
- // do something
- doSomething3();
-
- // user locker A in anohter thread
- m_threads.get_io_service().post(boost::bind(doSomething4));
- }
-
-
- // use locker A in thread 4
- void doSomething4()
- {
- Utility::WriteLocker lockerA(mutexA);
-
- // do something
- realDoSomething4();
- }