死锁的最常见情况
相互等待。
业务说明
对于简单的业务,可以规定一次只能锁定一个单元,但对于复杂业务,这种方法不可行。比如:金银互换,1金换10银。分4步:1,判断金币足够。 2,判断银币没到上限 3,金币减少。 4,银币增加。假定只有一金币,线程一执行步骤一,发现金币足够,碰巧线程被挂起; 线程二执行执行步骤一,发现金币足够。于是金币变成了-1。为了避免这种情况,必须同时锁金银。函数一,先锁金,再锁银;函数二,先锁银,再锁金。可能出现如下情况:线程一,锁了金,想锁银;线程二,锁了银,想锁金。
测试代码
namespace TEST1 { class CTestDeadLock { public: static UINT Test1(LPVOID) {// 模拟一金币换10银币 s_lock1.Lock(); Sleep(1); s_lock2.Lock(); if ( ( s_iJin > 0) && (s_iYin < 1000 ) ) { s_iJin--; s_iYin += 10; } s_lock1.UnLock(); s_lock2.UnLock(); return 0; } static UINT Test2(LPVOID) { s_lock2.Lock(); Sleep(1); s_lock1.Lock(); if ((s_iYin > 10) && (s_iJin < 1000)) { s_iYin-= 10; s_iJin++; } s_lock2.UnLock(); s_lock1.UnLock(); return 0; } protected: static SNMFC::CriticalReadWriteLock s_lock1; static SNMFC::CriticalReadWriteLock s_lock2; static int s_iJin; static int s_iYin; }; SNMFC::CriticalReadWriteLock CTestDeadLock::s_lock1; SNMFC::CriticalReadWriteLock CTestDeadLock::s_lock2; int CTestDeadLock::s_iJin =0; int CTestDeadLock::s_iYin = 0; } void CSNMFCDlg::OnBnClickedButton22() { AfxBeginThread(TEST1::CTestDeadLock::Test1, NULL); TEST1::CTestDeadLock::Test2(NULL); }
解决方法
上面的Sleep(1)是为了模拟碰巧被挂起。解决死锁的方法:按特定的顺序锁。如果要锁的单元多,调用者根本无法知道,先锁那个。
锁管理类
先设置标志,Lock的时候自动按顺序锁。
namespace TEST2 { //每个线程都自动分配锁 class IRWLocks : public SN::IReadWriteLock { public: virtual bool HasLock() = 0; virtual bool HasLockRead() = 0; }; //为每个线程都生产一把锁 class CCriticalRWLocks : public IRWLocks { protected: class CLockInfo : public SNMFC::CriticalReadWriteLock { public: CLockInfo() { m_iLockNum = 0; m_iLockReadNum = 0; } virtual void Lock() override { SNMFC::CriticalReadWriteLock::Lock(); m_iLockNum++; } virtual void UnLock() override { m_iLockNum--; SNMFC::CriticalReadWriteLock::UnLock(); } virtual void LockRead() override { m_iLockReadNum++; SNMFC::CriticalReadWriteLock::LockRead(); } virtual void UnLockRead() override { m_iLockReadNum--; SNMFC::CriticalReadWriteLock::UnLockRead(); } bool HasLock()const { return m_iLockNum > 0; } bool HasLockRead()const { return m_iLockReadNum > 0; } protected: int m_iLockNum ; int m_iLockReadNum ; }; public: CCriticalRWLocks() { Init(); } virtual void Lock() override { GetLockByCurrentThreadID()->Lock(); } virtual void UnLock() override { GetLockByCurrentThreadID()->UnLock(); } virtual void LockRead() override { GetLockByCurrentThreadID()->LockRead(); } virtual void UnLockRead() override { GetLockByCurrentThreadID()->UnLockRead(); } bool HasLock() override { return GetLockByCurrentThreadID()->HasLock(); } bool HasLockRead() override { return GetLockByCurrentThreadID()->HasLockRead(); } protected: CLockInfo* GetLockByCurrentThreadID() { SN::CLockHlp lock(m_lock); DWORD dThreadID = ::GetCurrentThreadId(); if (m_mLocks.end() == m_mLocks.find(dThreadID)) { m_mLocks[dThreadID] = new CLockInfo(); } return m_mLocks[dThreadID] ; } virtual void Init() { m_lock.Init(); } std::map<int, CLockInfo*> m_mLocks; SNMFC::CCriticalSection m_lock; }; template<class DATA> class CLockUnit { public: DATA* GetData() { if (!m_locks.HasLock()) { return NULL; } return &m_data; } const DATA* GetConstData() { if (!m_locks.HasLockRead()) { return NULL; } return &m_data; } CCriticalRWLocks m_locks; private: DATA m_data; }; //确保以某种顺序,加锁,解锁 以防止死锁 class CLockUnitManage { public: CLockUnitManage() { m_bHasLock = FALSE; } void SetLockFlag(SN::IReadWriteLock& lock) { if (m_bHasLock) { return; } m_mLocks[&lock] = 2; } void SetLockReadFlag(SN::IReadWriteLock& lock) { if (m_bHasLock) { return; } m_mLocks[&lock] = 1; } void BeginLock() { if (m_bHasLock) { return; } m_bHasLock = TRUE; for (std::map<SN::IReadWriteLock*, int>::const_iterator it = m_mLocks.begin(); it != m_mLocks.end(); ++it) { if (2 == it->second) { it->first->Lock(); } else if (1 == it->second) { it->first->LockRead(); } } } void EndLock() { if (!m_bHasLock) { return; } m_bHasLock = FALSE; for (std::map<SN::IReadWriteLock*, int>::const_iterator it = m_mLocks.begin(); it != m_mLocks.end(); ++it) { if (2 == it->second) { it->first->UnLock(); } else if (1 == it->second) { it->first->UnLockRead(); } } } ~CLockUnitManage() { EndLock(); } protected: std::map<SN::IReadWriteLock*,int> m_mLocks; bool m_bHasLock;//锁定状态下,解锁前无法 ,增加标志和锁。 }; CLockUnit<CString> g_strName; CLockUnit<int> g_iAge; class CTestDeadLock { public: static UINT Test1(LPVOID) { CLockUnitManage lockManages; lockManages.SetLockFlag(g_strName.m_locks); lockManages.SetLockFlag(g_iAge.m_locks); lockManages.BeginLock(); CString* pStr = g_strName.GetData(); int* pI = g_iAge.GetData(); *pStr += _T("A"); *pI += 1; Sleep(1); return 0; } static UINT Test2(LPVOID) { CLockUnitManage lockManages; lockManages.SetLockFlag(g_iAge.m_locks); lockManages.SetLockFlag(g_strName.m_locks); lockManages.BeginLock(); CString* pStr = g_strName.GetData(); int* pI = g_iAge.GetData(); *pStr += _T("A"); *pI += 1; Sleep(1); return 0; } }; } void CSNMFCDlg::OnBnClickedButton23() { for (int i = 0; i < 100; i++) { AfxBeginThread(TEST2::CTestDeadLock::Test1,NULL); AfxBeginThread(TEST2::CTestDeadLock::Test2, NULL); } TEST2::CTestDeadLock::Test2(NULL); }