優雅的讓一個類在線程安全和線程非安全間切換

一個良好的多線程庫,不應當一刀切的全加鎖。因爲有些時候,雖然是多線程環境,但可能依照設計一個類只會被一個線程操作,這個時候加鎖是多餘的,純浪費性能,但另一些場景又需要它是線程安全的。
 
假設有一個類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,看起來是不是相對優雅得體些了?
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章