Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1775960
  • 博文数量: 198
  • 博客积分: 4088
  • 博客等级: 上校
  • 技术积分: 2391
  • 用 户 组: 普通用户
  • 注册时间: 2011-05-15 16:29
个人简介

游戏开发,系统架构; 博客迁移到:http://www.jianshu.com/u/3ac0504b3b8c

文章分类

全部博文(198)

文章存档

2017年(1)

2016年(12)

2015年(1)

2014年(3)

2013年(13)

2012年(18)

2011年(150)

分类: C/C++

2014-01-17 10:42:02

前不久在工作中遇到一个死锁的问题,特记录下。



假设有这样的一个场景, 你调用某个接口, 这个接口调用的过程中,会上两个读写锁,上锁的顺序是先A后B。然后在底层网络io回调的时候,也会使用到这两个锁,上锁的顺序是先B后A。 那么就存在这样一种情况。假如线程T1调用接口,刚上完A锁,还没有上B锁。底层回调线程T2刚上完B锁,还没有上A锁。这个时候,线程T1要申请上B锁,但是B锁已经被线程T2占用,无法成功,T1线程阻塞,无法及时将A锁释放,线程T2要申请上A锁,但是因为A锁被T1线程占用,这样T2线程也阻塞了,无法释放B锁。这样就出现相互等待对方释放锁资源的问题,从而死锁了。

  1. // call interface in thread 1
  2. void callinterface()
  3. {
  4.     Utility::WriterLocker lockerA(mutexA);
  5.         
  6.     // do something
  7.     doSomething1();
  8.     
  9.     Utility::WriteLocker lockerB(mutexB);
  10.         
  11.     // do something
  12.     doSomething2();
  13.     
  14.     // io service    
  15.     
  16.     doIoService();
  17.     
  18. }


  19. // ioservice call back in thread 2
  20. void callback()
  21. {
  22.     Utility::WriterLocker lockerB(mutexB);
  23.         
  24.     // do something
  25.     doSomething3();
  26.     
  27.     Utility::WriteLocker lockerA(mutexA);
  28.         
  29.     // do something
  30.     doSomething4();
  31. }

解决这个问题的方法,可以有两种方法。
一、假如锁A和锁B实在同一个模块,我们可以使用递归锁代替读写锁,递归锁的特性就是,同一个线程可以进同一个锁多次, 不过要注意的是,锁A和锁B要使用相同的递归锁互斥对象

  1. // user recursive locker

  2. // call interface in thread 1
  3. void callinterface()
  4. {
  5.     Utility::RecursiveLocker lockerA(recursive_mutex);
  6.         
  7.     // do something
  8.     doSomething1();
  9.     
  10.     Utility::RecursiveLocker lockerB(recursive_mutex);
  11.         
  12.     // do something
  13.     doSomething2();
  14.     
  15.     // io service    
  16.     
  17.     doIoService();
  18.     
  19. }


  20. // ioservice call back in thread 2
  21. void callback()
  22. {
  23.     Utility::RecursiveLocker lockerB(recursive_mutex);
  24.         
  25.     // do something
  26.     doSomething3();
  27.     
  28.     Utility::RecursiveLocker lockerA(recursive_mutex);
  29.         
  30.     // do something
  31.     doSomething4();
  32. }

二、 锁A和锁B在不同的模块,比如锁B是在某个dll中。这个时候可以这样,如下图


调用接口的时候,使用锁A在线程T1,然后使用锁B的过程放在另一个线程T2。回调的时候,使用锁B的时候,在线程T3,使用锁A的时候,在线程T4。这样就不会出现相互等待对方释放锁资源的问题,从而避免了死锁.


  1. // use locker A in thread 1
  2. void callinterface()
  3. {
  4.     // use locker A
  5.     Utility::WriterLocker lockerA(mutexA);
  6.         
  7.     // do something
  8.     doSomething1();
  9.     
  10.     // user locker B in anohter thread
  11.     m_threads.get_io_service().post(boost::bind(doSomething2));
  12.     
  13. }

  14. // use locker B in thread 2
  15. void doSomething2()
  16. {
  17.     Utility::WriteLocker lockerB(mutexB);
  18.         
  19.     // do something
  20.     realDoSomething2();
  21.     
  22.     // io service    
  23.     
  24.     doIoService();
  25. }


  26. // ioservice call back in thread 3, user locker B
  27. void callback()
  28. {
  29.     Utility::WriterLocker lockerB(mutexB);
  30.         
  31.     // do something
  32.     doSomething3();
  33.     
  34.     // user locker A in anohter thread
  35.     m_threads.get_io_service().post(boost::bind(doSomething4));
  36. }


  37. // use locker A in thread 4
  38. void doSomething4()
  39. {
  40.     Utility::WriteLocker lockerA(mutexA);
  41.         
  42.     // do something
  43.     realDoSomething4();
  44. }


阅读(4309) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~