网上很容易找到一个用boost::shared_mutex来作为读写锁的例子:
- typedef boost::shared_mutex rwmutex;
- typedef boost::shared_lock<rwmutex> readLock;
- typedef boost::unique_lock<rwmutex> writeLock;
typedef boost::shared_mutex rwmutex;
typedef boost::shared_lock<rwmutex> readLock;
typedef boost::unique_lock<rwmutex> writeLock;
一直没有怀疑过这个做法,直到最近项目中出现一个死锁问题,查了很久才发现是上面这种写法造成的。
写测试代码还原死锁的情景如下:
- #include <boost/thread/shared_mutex.hpp>
- #include <boost/thread/shared_lock_guard.hpp>
- #include <boost/thread.hpp>
- #include <iostream>
- #include <Windows.h>
- using namespace std;
- typedef boost::shared_mutex rwmutex;
- typedef boost::shared_lock<rwmutex> readLock;
- typedef boost::unique_lock<rwmutex> writeLock;
-
- rwmutex m_mutex;
- void func1()
- {
- writeLock lock1(m_mutex);
- cout << "fffffffffffffff" << endl;
- }
-
- int main()
- {
- {
- boost::thread tt(func1);
- readLock lock(m_mutex);
- {
- Sleep(3000);
- readLock lock(m_mutex);
- cout << "rrrrrrrrrrrrrrrrrr" << endl;
- }
- }
- }
#include <boost/thread/shared_mutex.hpp>
#include <boost/thread/shared_lock_guard.hpp>
#include <boost/thread.hpp>
#include <iostream>
#include <Windows.h>
using namespace std;
typedef boost::shared_mutex rwmutex;
typedef boost::shared_lock<rwmutex> readLock;
typedef boost::unique_lock<rwmutex> writeLock;
rwmutex m_mutex;
void func1()
{
writeLock lock1(m_mutex);
cout << "fffffffffffffff" << endl;
}
int main()
{
{
boost::thread tt(func1);
readLock lock(m_mutex); // 加读锁
{
Sleep(3000); // 故意睡眠3秒将线程切换出去
readLock lock(m_mutex); // 醒来后再次加读锁
cout << "rrrrrrrrrrrrrrrrrr" << endl;
}
}
}
情形描述如下:
1:主线程先给m_mutex加读锁。
2:主线程故意睡眠3秒将执行权限切换出去。
3:func1线程获得执行机会,尝试加写锁被挂起。因为主线程已经有一个读锁占用,写锁必须等待这个读锁释放才能进入。
4:主线程睡眠3秒醒来,后续代码想获取一个读锁。由于这个时候已经有一个写锁在等待进入,那么这个读锁排队在写锁后面,同样被挂起。
5:主线程和func1线程都被挂起了,发生死锁。
上面的测试代码,如果中间没有sleep(3000),出现死锁的概率很低,但总归是存在死锁的可能。
总结:
以前一直认为readLock锁和递归锁一样,在同一个线程多次进入没有关系才会造成上面的死锁出现。所以,boost::shared_lock使用要小心,千万不要同一个线程多次进入。