ACE包含一个令牌子系统(Token Library),令牌与一般的锁类似,但该系统具有检测死锁的能力,在编译ACE时需要在config.h文件中定义如下宏来生成该子系统:ACE_HAS_TOKENS_LIBRARY。具体而言代码是这样:
- #include "stdafx.h"
- #include "ace/Task.h"
- #include "ace/Local_Tokens.h"
- #include "ace/Token_Manager.h"
- //#define IGNORE_DEADLOCK
- class task : public ACE_Task_Base
- {
- public:
- task(char const* name1, char const* name2)
- : name1_(name1)
- , name2_(name2)
- {
- }
- virtual int svc()
- {
- #if defined(IGNORE_DEADLOCK)
- ACE_Local_Mutex mutex1(name1_, 1, 1);
- ACE_Local_Mutex mutex2(name2_, 1, 1);
- #else
- ACE_Local_Mutex mutex1(name1_, 0, 1);
- ACE_Local_Mutex mutex2(name2_, 0, 1);
- #endif
- int result = mutex1.acquire();
- ACE_DEBUG((LM_DEBUG, "(%t) mutex1 acquired %d!\n", result));
- ACE_OS::sleep(2);
- {
- int result = mutex2.acquire();
- ACE_DEBUG((LM_DEBUG, "(%t) mutex2 acquired %d, %u!\n", result, ACE_OS::last_error()));
- if(result == 0)
- mutex2.release();
- }
- if(result == 0)
- mutex1.release();
- return 0;
- }
- private:
- char const* name1_;
- char const* name2_;
- };
- int ACE_TMAIN(int argc, ACE_TCHAR* argv[])
- {
- // must call this to see the dead lock dump.
- ACE_Token_Manager::instance()->debug(1);
- task t1("A", "B");
- task t2("B", "A");
- t1.activate();
- ACE_OS::sleep(1);
- t2.activate();
- t1.wait();
- t2.wait();
- return 0;
- }
复制代码
这是一个刻意制造的死锁情况,程序输出如下:
(2844) acquired A
(2844) mutex1 acquired 0!
(3040) acquired B
(3040) mutex1 acquired 0!
(2844) waiting for B, owner is /YUNHAI/1392/3040, total waiters == 2
(3040) Deadlock detected.
/YUNHAI/1392/3040 owns B and is waiting for A.
/YUNHAI/1392/2844 owns A and is waiting for B.
(3040) mutex2 acquired -1, 36!
(3040) released B, owner is /YUNHAI/1392/2844, total waiters == 1
(2844) unblocking /YUNHAI/1392/2844.
(2844) mutex2 acquired 0, 10035!
(2844) released B, owner is no owner, total waiters == 0
(2844) released A, owner is no owner, total waiters == 0
可以看到,死锁情况被检测到了,线程3040占有B但想获取A,线程2844占有A但想获取B,从而形成死锁。这段代码摘自《ACE程序员指南》P232,稍有改动,可以参看原文获取更详细的信息。这里想说的是,如果你将程序中使用的ACE_Thread_Mutex/ACE_Recursive_Thread_Mutex 替换成ACE_Local_Mutex,并启动调试输出,就可以看到死锁时是哪些线程在竞争哪些锁了。ACE_Local_Mutex本身是可重入的递归锁,接口与ACE_Thead_Mutex完全一样,可以做无缝替换,最好定义一个typedef,方便在它们之间进行切换。
但需要注意的是,最大的锁数量与平台支持的TLS/TSS槽个数相关,例如在WIN32上,最多支持64个TLS槽,而Tokens Library的实现依赖于此,所以要确保你的平台可以支持那么多的锁,不然会死的很惨! |