场景描述
对于一个确定的资源
有多个线程允许使用
读的需求多 写的需求少
面对这样的场景 我们可以使用 读写锁
在这种场景下 若使用互斥锁 相对会更浪费资源
因为从逻辑上出发 对于一个确定资源 多个线程同时读取是不会出现错误的
读写锁的特点
1.当一个读线程加锁时
允许其他的读线程继续加锁 进行读数据的操作
所有的写线程不允许加锁 进入阻塞
如果读锁被上锁n次(有n个线程在进行读数据的操作)
那么必须解锁n次之后才允许上写锁
2.当一个写线程加锁时
不允许任何新的线程加锁 所有的读线程或写线程全部阻塞
Linux环境中代码如下
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<pthread.h>
#include<unistd.h>
//8个线程共同使用的全局变量
int num = 0;
//读写锁变量
pthread_rwlock_t rwlock;
//读线程
void *fun_read(void *arg)
{
//获取线程的编号
int index = (int)(long)arg;
while(1)
{
//加读写锁 读锁
pthread_rwlock_rdlock(&rwlock);
printf("线程%d读取num的值为%d\n", index, num);
//解锁
pthread_rwlock_unlock(&rwlock);
//随机睡眠1~2秒
sleep(random() % 2 + 1);
}
return NULL;
}
//写线程
void *fun_write(void *arg)
{
//获取线程的编号
int index = (int)(long)arg;
while(1)
{
//加读写锁 写锁
pthread_rwlock_wrlock(&rwlock);
num++;
printf("线程%d修改num的值为%d\n", index, num);
//解锁
pthread_rwlock_unlock(&rwlock);
//随机睡眠1~2秒
sleep(random() % 2 + 1);
}
return NULL;
}
int main(void)
{
int i = 0;
int ret = -1;
srandom(getpid());//设置随机数种子
//初始化读写锁
ret = pthread_rwlock_init(&rwlock, NULL);
if(0 != ret)
{
printf("pthread_rwlock_init failed\n");
return 1;
}
//创建8个线程
pthread_t tid[8];
//X64环境下 内存地址占用8个字节 与long类型相同
//传参时注意先转换为long类型
for(i = 0;i < 8;i++)
{
if(i < 6)
{
//创建6个读进程
pthread_create(&tid[i], NULL, fun_read, (void *)(long)i);
}
else
{
//创建2个写进程
pthread_create(&tid[i], NULL, fun_write, (void *)(long)i);
}
}
//回收所有线程资源
for(i = 0;i < 8;i++)
{
pthread_join(tid[i], NULL);
}
//销毁读写锁
pthread_rwlock_destroy(&rwlock);
return 0;
}
运行效果