利用shared_ptr和互斥鎖來模擬讀寫鎖

讀寫鎖

讀寫鎖有三種狀態:讀模式下加鎖狀態,寫模式下加鎖狀態,不加鎖狀態。一次只有一個線程可以佔有寫模式的讀寫鎖,但是多個線程可以同時佔有讀模式的讀寫鎖。當佔有讀寫鎖的讀鎖時,任何線程希望以寫模式對此鎖進行加鎖時都會阻塞,但是這種情況下,讀寫鎖通常會阻塞隨後的讀鎖請求,這樣可以避免讀模式鎖長期佔用,而等待的寫模式鎖一直得不到滿足。當讀寫鎖是寫加鎖狀態時,在這個鎖被解鎖之前,所有試圖對這個鎖加鎖的線程都會被阻塞。

#include<pthread.h>
int pthread_rwlock_init(pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *attr);
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
兩個函數成功返回0,失敗返回錯誤碼。
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);//讀模式的鎖
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);//寫模式的鎖
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);//解鎖
  • 缺點:讀寫鎖的開銷是非常大的,並且在大型項目中非常容易出錯,我們可能會在讀的時候不小心修改了數據,這是致命的錯誤,並且當我們需要寫加鎖的時候,讀鎖是需要一直等待寫完成的。
  • 在性能方面來說,讀寫鎖不見得比普通mutex更高效。無論如何reader lock加鎖的開銷不會比mutex lock小,因爲它要更新當前的reader的數目。如果臨界區很小,鎖競爭不激烈,往往mutex更快。

智能指針加鎖實現讀寫鎖的功能

這個時候我們就想到利用智能指針和鎖來模擬讀寫鎖提高性能。因爲智能指針中有引用計數,我們就利用這個特性來模擬。

  • 首先,當我們是隻讀的時候,我們就複製一個變量,這個時候智能指針的引用計數就大於1,當沒有人在讀的時候引用計數就等於1。
  • 當我們需要更改數據的時候(寫操作),我們就判斷其引用計數是否爲1,如果爲1則證明沒有人在訪問數據,我們在原數據進行更改即可。當大於1的時候就有人在讀取數據,我們就需要複製一個副本進行更改即可。
    具體操作就看下面的代碼實現:
這是我們自己實現的類,MutexlockGuard類就和c++11線程庫中的std::lock_guard()是相同的作用。構造時上鎖,析構時解鎖。
#include<iostream>
#include<thread>
#include<mutex>
#include<pthread.h>
#include<unistd.h>
#include<assert.h>
class Mutexlock
{
public:  
    Mutexlock() : holder_(0) 
    {
        pthread_mutex_init(&mutex_, NULL);
    }
    ~Mutexlock()
    {
        pthread_mutex_destroy(&mutex_);
    }
    //刪除拷貝構造和拷貝賦值函數
//    Mutexlock(const Mutexlock &) = delete;
    //Mutexlock operator = (const Mutexlock &) = delete;
    
    bool isLockedByThisThread()
    {
        return holder_ == pthread_self();
    }

    void assertLocked()
    {
        assert(isLockedByThisThread());        
    }

    void lock()
    {
        pthread_mutex_lock(&mutex_);
        holder_ = pthread_self();
    }

    void unlock()
    {
        holder_ = 0;
        pthread_mutex_unlock(&mutex_);
    }

    pthread_mutex_t* getPthreadMutex()
    {
        return &mutex_;
    }
private:  
    pthread_mutex_t mutex_;
    pid_t holder_;

};

class MutexlockGuard
{
public:  
    explicit MutexlockGuard(Mutexlock& mutex) : mutex_(mutex) 
    {
        mutex_.lock();        
    }
    ~MutexlockGuard()
    {
        mutex_.unlock();
    }
private:  
    Mutexlock mutex_;
};
class Foo;

typedef std::vector<Foo> Foolist;
typedef std::shared_ptr<Foolist> FoolistPtr;
Mutexlock mutex;
FoolistPtr g_foos;

void read_data()
{
    FoolistPtr foos;
    
    {
        MutexlockGuard lock(mutex);
        foos= g_foos;
        assert(!g_foos.unique());
    }
    //其它操作
}

void write_data(const Foo &f)
{
    MutexlockGuard lock(mutex);
    //判斷引用計數是否爲1
    if(!g_foos.unique())
    {
        //如果有其它人正在讀,我們則複製一份,在副本上進行修改
        g_foos.reset(new Foolist(*g_foos));  //當reset之後,原g_foos的引用計數會-1,所以原始的當讀操作完畢後會析構掉。
        std::cout << "copy the whole list\n";
    }
    assert(g_foos.unique());
    g_foos->push_back(f);
}

上述就是我們用shared_ptr和互斥鎖實現的讀寫鎖操作。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章