一個良好的多線程庫,不應當一刀切的全加鎖。因爲有些時候,雖然是多線程環境,但可能依照設計一個類只會被一個線程操作,這個時候加鎖是多餘的,純浪費性能,但另一些場景又需要它是線程安全的。
假設有一個類X:
class X
{
public:
void xoo();
};
這裏總結幾個常見的做法:
1.本身不加鎖,由調用者來加鎖,壞處是如果多數場景都是加鎖的,由會產生重複代碼
class Z
{
public:
void zoo()
{
LockHelper<CLock> lh(_lock); // 調用者Z來加鎖
_x.xoo();
};
private:
CLock _lock;
X _x;
};
2.類的實現中,加if判斷
class X
{
public:
X(bool is_threadsafe)
:_is_threadsafe(is_threadsafe)
{
}
void xoo()
{
if (_is_threadsafe)
{
// 根據條件加鎖
_lock.lock();
}
。。。。。。
if (_is_threadsafe)
{
_lock.unlock();
}
}
private:
bool _is_threadsafe;
CLock _lock;
};
3.分成兩個類,第一個類是無鎖的,第二個類通過聚合第一個類,並用鎖包裝一下
class RawX
{
public:
void xoo()
{
// RawX總是不加鎖
}
};
class SafeX
{
public:
void xoo()
{
LockHelper<CLock> lh(_lock); // 總是加鎖
_raw_x.xoo();
}
private:
CLock _lock;
RawX _raw_x;
};
下面提出一種在mooon中使用的相對更優雅的方法,引用一個空鎖類CNullLock,它僅提供鎖的接口,加鎖和解鎖函數體都是空的:
class CNullLock
{
public:
void lock()
{
}
void unlock()
{
}
};
接下來看新的X的實現,需要將它變成一個模板類:
template <class Lock>
class X
{
public:
void xoo()
{
LockHelper<CLock> lh(_lock); // 這裏並不一定真是加鎖
}
};
如果需要X是線程安全的,可以這樣使用:
X<CLock> x;
如果不需要X是線程安全的,則可以如下使用:
X<CNullLock> x;
這樣的一個X,看起來是不是相對優雅得體些了?