引言
本文为第十三篇,线程同步之读写锁,读写锁也是解决线程同步的方法之一,在前边的两篇文章中国已经介绍了互斥量和自旋锁两种方法。读写锁的原理也和前边两种锁类似,但是读写锁做了一些改进
读写锁
读写锁的改进是从以下几个点进行考量的,其中最重要的是对临界资源的考量。在复杂的开发环境中,很可能会出现对临界资源 多读少写 的情况。比如说一个数据库中的表,存储的主要是一些历史数据,对于这些历史数据,一般都是进行查询,很少进行修改,那么这个存储历史数据的表就属于多读少写的临界资源。对于读取的时候 并不会改变临界资源的值 ,如果我们每一次去读或者写的时候都给它加锁,这样效率是很低的。那么这个时候就应该考虑是否有效率更高的方法?这个时候就产生了 读写锁
读写锁介绍
- 读写锁是一种特殊的自旋锁
- 允许多个读者同时访问资源,以提高读性能
- 对于写操作是互斥的(不允许多个写操作同时访问同一资源)
关于读写锁的模型
对于读写锁,它 允许多个读者同时去读取临界资源 ,因此下图中的读线程 1、2、3 可以同时读取临界资源,但是在读取的同时,它不会允许写操作去访问临界资源。因为读取的时候并不会改变临界资源,而写操作可能会改变临界资源的值,因此 在读写锁中读和写是互斥的,读和读之间是不互斥的
读写锁示例
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<pthread.h>
#include<vector>
// 临界资源
int num=0;
// 定义读写锁
pthread_rwlock_t relock=PTHREAD_RWLOCK_INITIALIZER;
void *reader(void*){
int times=10000000;
while(times--){
// 在读操作之前加读锁
pthread_rwlock_rdlock(&rdlock);
if(times%1000==0){usleep(10);
}
// 释放读锁
pthread_rwlock_unlock(&rdlock);
}
}
void *writer(void*){
int times=10000000;
while(times--){
// 加写锁
pthread_rwlock_wrlock(&rdlock);
num+=1;
pthread_rwlock_unlock(&rdlock);
}
}
int main()
{printf("Start in main function.");
// 定义三个线程
pthread_t thread1,thread2, thread3;
// 两个执行读操作,一个执行写操作
pthread_create(&thread1, NULL, &reader, NULL);
pthread_create(&thread2, NULL, &reader, NULL);
pthread_create(&thread3, NULL, &writer, NULL);
pthread_join(&thread1, NULL);
pthread_join(&thread2, NULL);
pthread_join(&thread3, NULL);
// 打印临界资源的值
printf("Print in main function: num = %d\n", num);
return 0;
}
因为上边提到,读写锁对于 多读少写 的情况下,会有很明显的性能提升的,此时就可以验证一下,运行上边使用的读写锁的程序,查看运行时间如下:
现在将读写锁换成互斥量,然后看一下执行时间
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<pthread.h>
#include<vector>
// 临界资源
int num=0;
// 初始化互斥量
pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;
// 定义读写锁
//pthread_rwlock_t relock=PTHREAD_RWLOCK_INITIALIZER;
void *reader(void*){
int times=10000000;
while(times--){
// 在读操作之前加读锁
//pthread_rwlock_rdlock(&rdlock);
pthread_mutex_lock(&mutex);
if(times%1000==0){usleep(10);
}
// 释放读锁
//pthread_rwlock_unlock(&rdlock);
pthread_mutex_unlock(&mutex);
}
}
void *writer(void*){
int times=10000000;
while(times--){
// 加写锁
//pthread_rwlock_wrlock(&rdlock);
pthread_mutex_lock(&mutex);
num+=1;
//pthread_rwlock_unlock(&rdlock);
pthread_mutex_unlock(&mutex);
}
}
int main()
{printf("Start in main function.");
// 定义三个线程
pthread_t thread1,thread2, thread3;
// 两个执行读操作,一个执行写操作
pthread_create(&thread1, NULL, &reader, NULL);
pthread_create(&thread2, NULL, &reader, NULL);
pthread_create(&thread3, NULL, &writer, NULL);
pthread_join(&thread1, NULL);
pthread_join(&thread2, NULL);
pthread_join(&thread3, NULL);
// 打印临界资源的值
printf("Print in main function: num = %d\n", num);
return 0;
}
执行结果:
可以看见,对于多读少写的临界资源,使用读写锁的效率是使用互斥量的大概 5 倍
PHP 中读写锁相关 API:https://www.php.net/manual/zh…
在快速变化的技术中寻找不变,才是一个技术人的核心竞争力。知行合一,理论结合实践