yoogera 发表于 2011-2-26 21:09:21

分享一个Message_Block造成死锁的问题

本帖最后由 yoogera 于 2011-2-26 21:10 编辑

事情是这样的:
1、项目中使用Terabit作为网络收发模型,由于会存在将一个Message_Block转发到多个网络端,为了减少内存拷贝,需要使用到Message_Block底duplicate方法,对于Message_Block的duplicate方法,在多线程模式下需要加锁,而Terabit的默认生成的Message_Block是没有加锁的(锁为空),所以万不得已的情况下修改了Terabit的Message_Factory_T的实现,使其生成加锁的Message_Block。
2、在生成加锁的Message_Block的时候,由于ACE的Message_Block不能连带管理传递给他的锁对象的生命周期,也就是构造Message_Block的时候传递给他的Lock对象要自己负责删除,无法通过设置Message_Block而使Message_Block之析构的时候也将锁对象删除了。为了提高Message_Block的并发度,我不想让所有的Message_Block共享同一个锁对象,所以我选择的是预先在一个全局对象中生成一个锁队列(比如1024个锁),每次生产Message_Block的时候就从中选择下一个锁供他使用,这样就既能很好的管理锁对象生命周期,又不至于不相关的Message_Block在使用锁定时候还要发生竞争。

周一的时候,程序中可能发生死锁的情况,而且发生死锁也是偶然的,时而发生,而且数据量大的时候发生的比较多一点,断线后重连的时候发生的概率也大一点。
1、开始的时候怀疑是Terabit的使用中关于Read和Write的时候发生死锁,所以将程序中两个重要的锁的锁定范围缩小到最小,而且对于Protocol对象使用了引用计数,以便在向Protocol中写入数据的时候不至于将Protocol锁住,但是现象还是发生。
2、后来在vs中查看锁对象的占用,发现之所以线程锁住,是由于Terabit在释放Message_Block的时候等待了。而且根据提示是两个Message_Block的线程相互等待,经过对Terabit的代码分析发现,在Terabit中写消息的时候会将接受到底Message_Block串联起来(cont函数调用),然后在将他们发送完毕后依次调用每个Message_Block的release函数,将每个Message_Block释放掉。至此,联系到上面的对Message_Block的锁机制,发现可能的一个死锁情况:
      a) 现在锁队列中两个锁L1和L2
      b) 一个发送线程中的两个Message_Block为M11,M12,程序中为他们分配的锁为L1,L2
      c) 另一个发送线程中的两个Message_Block为M21,M22,程序中为他们分配的锁为L2,L1
      d) 则在b线程析构Message_Block的时候,会依次获取L1,L2
      e) 则在c线程析构Message_Block的时候,会依次获取L2,L1
      f) 上面两个线程就发生了死锁,并且无法自行解开

至此,问题的原因已经找到了,但是ACE的Message_Block无法自动管理他的Lock对象,所以此处退而求其次,将Message_Factory_T中的锁队列修改一个一个锁,让所有的Message_Block共享一个锁。

修改,测试,问题解决,也耗费了我两天的时间了~~~

在此mark一下,在使用Message_Block的锁机制的时候一定要注意,Message_Block本身也是可以连接成一个队列的,所以如果锁之间也会存在这样的先后关系的话,就特别容易锁住。

huzia 发表于 2011-2-28 09:14:55

好文章,学习了。

azoiu 发表于 2011-3-10 18:26:30

学习~~~~

adias 发表于 2011-3-10 19:27:11

学习了~~~~

sgx9988 发表于 2013-4-19 10:08:31

老大,这个枷锁的Message_Block如何生成,能不能给个例子讲讲啊,譬如ACE_Message_Block (msg_len, ACE_Message_Block::MB_DATA, 0, 0, 0, lock_p),lock_p如何初始化等。
页: [1]
查看完整版本: 分享一个Message_Block造成死锁的问题