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