软件架构师何志丹

 

死锁的最常见情况

相互等待。 

业务说明

对于简单的业务,可以规定一次只能锁定一个单元,但对于复杂业务,这种方法不可行。比如:金银互换,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);
}

posted on 2023-02-16 11:00  闻缺陷则喜何志丹  阅读(7)  评论(0编辑  收藏  举报  来源