在多线程中有这么一种情况,就是有着少量的写,但是却存在频繁的读取数据。
这种情况如果采取互斥锁那种方式解决的话,就会显得有点降低程序的效率
这时就可以使用读写锁来完成
适用场景
少量写 + 大量的读
- 特点
用于不同的读取线程,可以获取读模式下的读写锁而并行的运行。
读写锁的三种状态
- 读模式下加锁
- 写模式下加锁
- 不加锁
加锁规则
写的情况
- 一次只有一个线程可以占用写模式的读写锁。
- 一个执行流在进行写的时候,其他执行流既不能写,也不能读,只能陷入阻塞状态。
读的情况
- 多个读取线程可以同时占用读模式下的读写锁,在读写锁的内部有一个引用计数器
引用计数:标识着有多少以读模式打开的读写锁线程
- 每当一个线程以读模式打开读写锁的时候,引用计数器+1
- 当一个线程释放以读模式打开的读写锁的时候,引用计数器-1
作用:判断释放读模式打开的读写锁时,能否完全解锁
- 如果引用计数器完全减为0,表示当前没有读模式的线程占用读写锁,那么以读模式的读写锁就解锁了
- 引用计数器大于0,表明还有线程以读模式打开读写锁,就会和想要以写模式打开的读写锁进行互斥。
写独占,读共享,读锁优先级高
操作接口
定义
pthread_rwlock_t
初始化
int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock,
const pthread_rwlockattr_t *restrict attr);
attr 共有 3 种选择
PTHREAD_RWLOCK_PREFER_READER_NP
(默认设置) 读者优先,可能会导致写者饥饿情况PTHREAD_RWLOCK_PREFER_WRITER_NP
写者优先,目前有 BUG,导致表现行为和
PTHREAD_RWLOCK_PREFER_READER_NP
一致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);
完整程序
- makefile
rwlock:rwlock.cpp
g++ $^ -o $@ -g -lpthread -std=c++11
- rwlock.cpp
#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;
}