适用场景
少量写+大量的读
注意这个是先是读优先的
读写锁的三种状态
1.读模式下的加锁
2.写模式下的加锁
3.不加锁
加锁规则
写的情况:
1.一次只有一个线程可以占用写模式的读写锁
2.一个执行流在写的时候,其他执行流既不能写,也不能读,只能陷入阻塞状态
3.多个读取线程可以同时占用读模式下的读写锁,在读写锁的内部有一个引用计数器
引用计数:标识着有多少以读模式打开的读写锁线程
1.每当一个线程以读模式打开读写锁的时候,引用计数器+1
2.当一个线程释放以读模式打开读写锁的时候,引用计数器-1
作用:判断释放读模式打开的读写锁时,能否完全解锁
1.如果引用计数器完全减为0,表示当前没有读模式的线程占用读写锁,那么以读模式的读写锁就解锁了
2.引用计数器大于0,表明还有线程以读模式打开读写锁,就会和想要以写模式打开的读写锁进行互斥
当前锁的状态 | 读锁请求 | 写锁请求 |
---|---|---|
无锁 | 可以 | 可以 |
读锁 | 可以 | 阻塞 |
写锁 | 阻塞 | 阻塞 |
写独占,都共享,读锁优先级高
操作接口
定义
pthread_rwlock_t
初始化
int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock,const pthread_rwlockattr_t *restrict attr);
attr共有三种选择
1.PTHREAD_RWLOCK_PREFER_READER_NP (默认设置) 读者优先,可能会导致写者饥饿情况
2.PTHREAD_RWLOCK_PREFER_WRITER_NP 写者优先,目前有 BUG,导致表现行为和 PTHREAD_RWLOCK_PREFER_READER_NP 一致
3.PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP 写者优先,但写者不能递归加锁
加锁
以读模式打开,进行加锁
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
以写模式打开,进行加锁
int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
解锁
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
销毁
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
完整代码:
#include <cstdio>
#include <cstdlib>
#include <unistd.h>
#include <vector>
#include <string>
#include <pthread.h>
#include <iostream>
using std::cout;
using std::vector;
using std::string;
using std::endl;
volatile int ticket = 1000;//保持变量ticket对内存可见性,防止编译器过度优化
pthread_rwlock_t rwlock; //定义全局的读写锁变量
//给线程进行标号的结构体
struct ThreadAttr {
pthread_t tid;
size_t id;
};
//初始化读写锁
void init_rwlock(int state) {
//write priority
if(state == 0) {
pthread_rwlockattr_t attr;
pthread_rwlockattr_init(&attr);
//定义attr变量为 写优先,但是写的时候不能递归加锁
pthread_rwlockattr_setkind_np(&attr,PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP);
pthread_rwlock_init(&rwlock,&attr);
pthread_rwlockattr_destroy(&attr);
}
else { //read priority && write hunger
pthread_rwlock_init(&rwlock,nullptr);
}
}
void join_threads(vector<ThreadAttr>& vec) {
for(auto& eoch : vec) {
pthread_join(eoch.tid,nullptr);
}
}
void* readStart(void* arg)
{
ThreadAttr* info = (ThreadAttr*)arg;
while(1) {
pthread_rwlock_rdlock(&rwlock);
if(ticket <= 0) {
pthread_rwlock_unlock(&rwlock);
break;
}
cout<<"thread reader: "<<info->id<<" : "<<ticket<<endl;
pthread_rwlock_unlock(&rwlock);
sleep(1);
}
return nullptr;
}
void* writeStart(void* arg)
{
ThreadAttr* info = (ThreadAttr*) arg;
while(1) {
pthread_rwlock_wrlock(&rwlock);
if(ticket <= 0) {
pthread_rwlock_unlock(&rwlock);
break;
}
cout<<"thread write "<<info->id<<" : "<<--ticket<<endl;
pthread_rwlock_unlock(&rwlock);
sleep(1);
}
return nullptr;
}
int main() {
const size_t reader_nr = 2;
const size_t writer_nr = 1000;
vector<ThreadAttr> readers(reader_nr);
vector<ThreadAttr> writers(writer_nr);
init_rwlock(0);//0-->read priority 1--> write priority
for(size_t i = 0; i < readers.size(); i++) {
readers[i].id = i;
pthread_create(&readers[i].tid,nullptr,readStart,(void*)&readers[i]);
}
for(size_t i = 0; i < writers.size(); i++) {
writers[i].id = i;
pthread_create(&writers[i].tid,nullptr,writeStart,(void*)&writers[i]);
}
//thread wait
join_threads(writers);
join_threads(readers);
pthread_rwlock_destroy(&rwlock);
return 0;
}
使用C++11实现读写锁
使用互斥锁和条件变量实现读写锁
class readwrite_lock
{
public:
readwrite_lock()
: stat(0)
{
}
void readLock()
{
mtx.lock();
while (stat < 0)
cond.wait(mtx);
++stat;
mtx.unlock();
}
void readUnlock()
{
mtx.lock();
if (--stat == 0)
cond.notify_one(); // 叫醒一个等待的写操作
mtx.unlock();
}
void writeLock()
{
mtx.lock();
while (stat != 0)
cond.wait(mtx);
stat = -1;
mtx.unlock();
}
void writeUnlock()
{
mtx.lock();
stat = 0;
cond.notify_all(); // 叫醒所有等待的读和写操作
mtx.unlock();
}
private:
mutex mtx;
condition_variable cond;
int stat; // == 0 无锁;> 0 已加读锁个数;< 0 已加写锁
};
用mutex和condtion实现写优先的读写锁
class RWLock {
private:
pthread_mutex_t mxt;
pthread_cond_t cond;
int rd_cnt;//等待读的数量
int wr_cnt;//等待写的数量
public:
RWLock() :rd_cnt(0), wr_cnt(0) {
pthread_mutex_init(&mxt,NULL);
pthread_cond_init(&cond,NULL);
}
void readLock() {
pthread_mutex_lock(&mxt);
++rd_cnt;
while(wr_cnt > 0)
pthread_mutex_wait(&cond, &mxt);
pthread_mutex_unlock(&mxt);
}
void readUnlock() {
pthread_mutex_lock(&mxt);
--rd_cnt;
if (rd_cnt == 0 )
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mxt);
}
void writeLock() {
pthread_mutex_lock(&mxt);
++wr_cnt;
while (wr_cnt + rd_cnt >=2)
pthread_cond_wait(&cond, &mxt);
pthread_mutex_unlock(&mxt);
}
void writerUnlock() {
pthread_mutex_lock(&mxt);
--wr_cnt;
if(wr_cnt==0)
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mxt);
}
};