【linux编程】读写锁
读写锁
读写锁由「读锁」和「写锁」两部分构成,如果只读取共享资源用「读锁」加锁,如果要修改共享资源则用「写锁」加锁。所以,读写锁适用于能明确区分读操作和写操作的场景。
读写锁的工作原理是:
- 当「写锁」没有被线程持有时,多个线程能够并发地持有读锁,这大大提高了共享资源的访问效率,因为「读锁」是用于读取共享资源的场景,所以多个线程同时持有读锁也不会破坏共享资源的数据。
- 但是,一旦「写锁」被线程持有后,读线程的获取读锁的操作会被阻塞,而且其他写线程的获取写锁的操作也会被阻塞。
所以说,写锁是独占锁,因为任何时刻只能有一个线程持有写锁,类似互斥锁和自旋锁,而读锁是共享锁,因为读锁可以被多个线程同时持有。知道了读写锁的工作原理后,我们可以发现,读写锁在读多写少的场景,能发挥出优势。
另外,根据实现的不同,读写锁可以分为「读优先锁」和「写优先锁」。
读优先锁期望的是,读锁能被更多的线程持有,以便提高读线程的并发性,它的工作方式是:当读线程 A 先持有了读锁,写线程 B 在获取写锁的时候,会被阻塞,并且在阻塞过程中,后续来的读线程 C 仍然可以成功获取读锁,最后直到读线程 A 和 C 释放读锁后,写线程 B 才可以成功获取读锁。如下图:
而写优先锁是优先服务写线程,其工作方式是:当读线程 A 先持有了读锁,写线程 B 在获取写锁的时候,会被阻塞,并且在阻塞过程中,后续来的读线程 C 获取读锁时会失败,于是读线程 C 将被阻塞在获取读锁的操作,这样只要读线程 A 释放读锁后,写线程 B 就可以成功获取读锁。如下图:
归纳总结:
1. 函数原型:
#include <pthread.h>
pthread_rwlock_init(pthread_rwlock_t *restrict rwlock, const pthread_rwlockattr_t *restrict attr); // 初始化读写锁
pthread_rwlock_destroy(pthread_rwlock_t *rwlock); // 销毁读写锁
2. 函数原型:
#include <pthread.h>
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock); // 读锁
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock); // 写锁
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock); // 解锁
1.实例:
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
int h = 10000;
pthread_rwlock_t lock; //创建 读写锁
void *funwrite(void *arg)
{
while (10)
{
for (int i = 0; i < 10000; i++)
{
pthread_rwlock_wrlock(&lock); //加写锁
h++;
printf("write is %d\n", h);
pthread_rwlock_unlock(&lock); //解锁
sleep(2);
}
}
}
void *funread(void *arg)
{
while (10)
{
for (int i = 0; i < 10000; i++)
{
pthread_rwlock_rdlock(&lock); //加读锁
printf("read is %d\n", h);
pthread_rwlock_unlock(&lock); //解锁
sleep(3);
}
}
}
int main()
{
pthread_t p[8];
int i;
pthread_rwlock_init(&lock, NULL); // 初始化读写锁
for (i = 0; i < 3; i++)
pthread_create(&p[i], NULL, funwrite, NULL);
for (i = 3; i < 8; i++)
pthread_create(&p[i], NULL, funread, NULL);
for (int a = 0; a < 8; a++)
pthread_join(p[a], NULL);
pthread_rwlock_destroy(&lock); // 销毁
return 0;
}
输出: