情景:
多線程編程中,通常某些數據是在多個線程間共享的,由於線程的併發行,一個線程讀寫的過程中另一個線程也極大可能同時讀寫, 這必然造成公共資源的錯亂, 引起程序錯誤, 此時對公共資源的保護實在必行了, 需要使用公共資源之前, 先“鎖”住, 防止其它線程更改,等待當前使用完成“解鎖”後,其餘線程纔可以更改, 此種情形引出多種鎖模型,適用於不同情形:
1. 多線程爭搶修改資源,都需要獨佔式修改 – 互斥獨佔式訪問
2. 多個線程只是讀資源,並不修改 – 實際上不需要鎖
3. 某些線程修改,另一部分只是讀取 – 寫線程必須互斥獨佔式訪問,讀線程可共享式訪問
實際上述情景3也可以全部獨佔互斥訪問, 如果存在多個讀取線程, 獨佔式需要逐個加鎖、解鎖訪問,效率低下, 讀寫鎖Write/Read lock 應運而生。
讀寫鎖(Write/Read lock)
讀寫鎖也叫做共享-獨佔鎖,當讀寫鎖以讀模式鎖住時,它是以共享模式鎖住的,當它以寫模式鎖住時,它是以獨佔模式鎖住的。
那麼讀寫鎖如何實現呢? 在*nix 平臺藉助於pthread_rwlock_t 實現起來比較簡單, 選用“準”標準庫boost 中實現方式,代碼如下:
//! A light read/write mutex that maps directly onto POSIX threading library
class light_rw_mutex
{
private:
pthread_rwlock_t m_Mutex;
private:
light_rw_mutex(light_rw_mutex const&); // Noncopyable
light_rw_mutex& operator= (light_rw_mutex const&); // Noncopyable
public:
light_rw_mutex()
{
pthread_rwlock_init(&m_Mutex, NULL);
}
~light_rw_mutex()
{
pthread_rwlock_destroy(&m_Mutex);
}
void lock_shared()
{
pthread_rwlock_rdlock(&m_Mutex);
}
void unlock_shared()
{
pthread_rwlock_unlock(&m_Mutex);
}
void lock()
{
pthread_rwlock_wrlock(&m_Mutex);
}
void unlock()
{
pthread_rwlock_unlock(&m_Mutex);
}
};
有了上述參照,自己封裝讀寫鎖卻也不難。
class RLock
{
public:
RLock( light_rw_mutex & lock ) : mLock( lock )
{
mLock.lock_shared();
}
~RLock()
{
mLock.unlock_shared();
}
private:
light_rw_mutex & mLock; //注意這裏是引用
private: // disable
RLock();
RLock(const RLock &);
const RLock & operator = (const RLock &);
};
class WLock
{
public:
WLock(light_rw_mutex & lock ) : mLock( lock )
{
mLock.lock();
}
~WLock()
{
mLock.unlock();
}
private:
light_rw_mutex & mLock; //注意這裏是引用
private: // disable
WLock();
WLock(const WLock &);
const WLock & operator = (const WLock &);
};
既然有“準”標準庫boost, 也可以直接使用boost 已經實現的代碼,簡單方便:
//頭文件
#include <boost/thread/locks.hpp>
#include <boost/thread/shared_mutex.hpp>
// 簡單定義
typedef boost::shared_mutex Lock;
typedef boost::unique_lock< Lock > WriteLock;
typedef boost::shared_lock< Lock > ReadLock;
//使用example
Lock myLocker; // or light_rw_mutex myLocker;
void ReadFunction()
{
ReadLock rLock(myLocker); // or RLock rLock(myLocker);
//Read common resource
}
void WriteFunction()
{
WriteLock wLock(myLocker); // or WLock wLock(myLocker);
//Write common resource
}