1. 概念

  • 读写锁与互斥量类似。但是互斥量要么是锁住状态,要么就是不加锁状态,而且一次只有一个线程可以对其加锁
  • 不过读写锁允许更高的并行性,而且有更多的状态

读写锁可以有3种状态:

  • ①读模式下加锁
  • ②写模式下加锁
  • ③不加锁状态

2. 使用

  • 当读写锁是写加锁状态时:在这个锁被解锁之前,所有试图对这个锁加锁的线程都会阻塞(不论是读还是写)
  • 当读写锁是读加锁状态时:所有试图以读模式对它进行加锁的线程都可以得到访问权。但是任何希望以写模式对此锁进行加锁的线程都会阻塞,直到所有的线程释放它们的读锁为止

虽然各操作系统对读写锁的实现各不同,但当读写锁处于读模式锁住的状态,而这时有一个线程试图以写模式获取锁时,读写锁通常会阻塞随后的读模式锁请求。这样可以避免读模式锁长期占用,而等待的写模式锁请求一直得不到满足。

读写锁的应用:读写锁非常适合于对数据结构读的次数大于写的情况

  • 当读写锁在写模式下时:它所保护的数据结构就可以被安全的修改,因为一次只有一个线程可以在写模式下拥有这个锁
  • 当读写锁在读模式下时:只要线程先获取了读模式下的读写锁,该锁所保护的数据结构就可以被多个获得读模式锁的线程读取

3. 别名

读写锁也称为共享互斥锁

  • 当读写锁是读模式锁住时,就可以说成是以共享模式锁住的
  • 当它是写模式锁住时,就可以说成是以互斥模式锁住的

4. 变量

  • 读写锁数据类型:pthread_rwlock_t

5. 读写锁的初始化和释放

①静态初始化

  • 直接把pthread_rwlock_t互斥变量设置为常量PTHREAD_RWLOCK_INITIALIZER
  • 静态初始化读写锁变量只能拥有默认的读写锁属性,不能设置其他读写锁属性

例如

pthread_rwlock_t rwlock;
rwlock=PTHREAD_RWLOCK_INITIALIZER;//或者
pthread_rwlock_t *rwlock=(pthread_rwlock_t *)malloc(sizeof(pthread_rwlock_t));
*rwlock=PTHREAD_MUTEX_INITIALIZER;

②动态初始化

  • 静态初始化读写锁变量只能拥有默认读写锁属性,我们可以通过pthread_rwlock_init函数来动态初始化读写锁,并且可以在初始化时选择设置读写锁的属性
#include <pthread.h>
int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock,const pthread_rwlockattr_t *restrict attr);
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);// 返回值:成功返回0,否则返回错误编号

pthread_rwlock_init:

  • 功能:对读写锁变量进行初始化
  • 参数:
    • 参数1:初始化的读写锁
    • 参数2:读写锁初始化时的属性。如果用默认属性,此处填NULL

pthread_rwlock_destory:

  • 功能:读写锁变量的反初始化,释放销毁
  • 参数:读写锁变量
  • 如果在pthread_rwlock_destory之前就释放了读写锁占用的内存空间,那么分配给这个锁的资源就会丢失
  • 备注(重点):此函数只是反初始化读写锁变量,并没有释放内存空间,如果读写锁变量是通过malloc等函数申请的,那么需要在free掉读写锁变量之前调用pthread_rwlock_destory函数
pthread_rwlock_t rwlock;
pthread_rwlock_init(&rwlock,NULL);/*do something*/pthread_rwlock_destory(&rwlock);
pthread_rwlock_t* rwlock=(pthread_mutex_t*)malloc(sizeof(pthread_mutex_t));
pthread_rwlock_init(rwlock,NULL);/*do something*/pthread_rwlock_destory(rwlock);
free(rwlock);

6. 加锁与解锁函数

#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);// 返回值:成功返回0,否则返回错误编号
  • pthread_rwlock_rdlock:在读模式下锁定读写锁
  • pthread_rwlock_wrlock:在写模式下锁定读写锁
  • pthread_rwlock_unlock:不管以何种方式锁住读写锁,都可以用这个函数解锁

返回值的注意事项:

  • 各种实现可能会对共享模式下可获取的读写锁的次数进行限制,所以需要检查pthread_rwlock_rdlock的返回值
  • 即使pthread_rwlock_wrlock和pthread_rwlock_unlock有错误返回,而且从技术上来讲,在调用函数时应该总会检查错误返回,但是如果锁设计合理的话,就不需要检查它们。错误返回值的定义只是针对不正确使用读写锁的情况(如未经初始化的锁),或者试图获取已拥有的锁从而可能产生死锁的情况
  • 但是还需要注意,有些特定的实现可能会定义另外的错误返回
#include <pthread.h>
int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);//返回值:成功返回0;否则返回错误编号

这两个函数类似于pthread_mutex_trylock函数:

  • pthread_rwlock_tryrdlock:尝试获得读模式的读写锁,如果可以获取返回0,不可以获取出错返回EBUSY
  • pthread_rwlock_trywrlock:尝试获得写模式的读写锁,如果可以获取返回0,不可以获取出错返回EBUSY

这两个函数可以用于我们前面讨论的遵守某种锁层次但还不能完全避免死锁的情况

7. 示例

  • 下面的程序解释了读写锁的使用。作业请求队列由单个读写锁保护。这个例子给出了下图所示的一种可能的实现,多个工作线程获取单个主线程分配给它们的作业

#include <stdlib.h>
#include <pthread.h>struct job {struct job *j_next;struct job *j_prev;pthread_t j_id; /* tells which thread handles this job *//* ... more stuff here ... */
};struct queue {struct job *q_head;struct job *q_tail;pthread_rwlock_t q_lock;
};//Initialize a queue.
int queue_init(struct queue *qp)
{int err;qp->q_head = NULL;qp->q_tail = NULL;err = pthread_rwlock_init(&qp->q_lock, NULL);if (err != 0)return(err);/* ... continue initialization ... */return(0);
}//Insert a job at the head of the queue.
void job_insert(struct queue *qp, struct job *jp)
{pthread_rwlock_wrlock(&qp->q_lock);jp->j_next = qp->q_head;jp->j_prev = NULL;if (qp->q_head != NULL)qp->q_head->j_prev = jp;elseqp->q_tail = jp; /* list was empty */qp->q_head = jp;pthread_rwlock_unlock(&qp->q_lock);
}//Append a job on the tail of the queue.
void job_append(struct queue *qp, struct job *jp)
{pthread_rwlock_wrlock(&qp->q_lock);jp->j_next = NULL;jp->j_prev = qp->q_tail;if (qp->q_tail != NULL)qp->q_tail->j_next = jp;elseqp->q_head = jp; /* list was empty */qp->q_tail = jp;pthread_rwlock_unlock(&qp->q_lock);
}//Remove the given job from a queue.
void job_remove(struct queue *qp, struct job *jp)
{pthread_rwlock_wrlock(&qp->q_lock);if (jp == qp->q_head) {qp->q_head = jp->j_next;if (qp->q_tail == jp)qp->q_tail = NULL;elsejp->j_next->j_prev = jp->j_prev;} else if (jp == qp->q_tail) {qp->q_tail = jp->j_prev;jp->j_prev->j_next = jp->j_next;} else {jp->j_prev->j_next = jp->j_next;jp->j_next->j_prev = jp->j_prev;}pthread_rwlock_unlock(&qp->q_lock);
}//Find a job for the given thread ID.
struct job * job_find(struct queue *qp, pthread_t id)
{struct job *jp;if (pthread_rwlock_rdlock(&qp->q_lock) != 0)return(NULL);for (jp = qp->q_head; jp != NULL; jp = jp->j_next)if (pthread_equal(jp->j_id, id))break;pthread_rwlock_unlock(&qp->q_lock);return(jp);
}

8. 带有超时的读写锁

#include <pthread.h>
#include <time.h>
int pthread_rwlock_timedrdlock(pthread_rwlock_t *restrict rwlock,const struct timespec *restrict tsptr);
int pthread_rwlock_timedwrlock(pthread_rwlock_t *restrict rwlock,const struct timespec *restrict tsptr);//返回值:成功返回0;否则返回错误编号
  • 与互斥量一样,Single UNIX Specification提供了带有超时的读写锁加锁函数,使应用程序在获取读写锁时避免陷入永久阻塞状态
  • 如果时间到期超时时,我们不能获取锁,两个函数返回ETIMEOUT错误
  • 注意:这个时间值是一个绝对数而不是相对数。例如,假设愿意等待3分钟,那么不是把3分钟转换为timespec结构体,而是需要把当前时间加上3分钟再转换成timespec结构

linux进阶51——pthread_rwlock_t(读写锁)相关推荐

  1. Linux多线程的同步------读写锁

    前面介绍过Linux多线程同步的另外两个方法------互斥锁和信号量 Linux多线程的同步-----信号量和互斥锁_神厨小福贵!的博客-CSDN博客 下面来看一下读写锁: 读写锁和互斥锁都带有一个 ...

  2. linux线程同步(3)-读写锁

    一.概述                                                    读写锁与互斥量的功能类似,对临界区的共享资源进行保护!互斥量一次只让一个线程进入临界区, ...

  3. linux 进程 读写锁,linux 下实现高性能读写锁(read/write lock)

    前一篇文章分析了Windows slim read/write lock的工作原理.我们知道它的设计相当精妙,于是我们可以借鉴它的思路来设计linux下的读写锁. 在这个读写锁的设计上,需要注意的是l ...

  4. Linux下pthread的读写锁的优先级问题

    有这么一个情况:有一个C实现的HashMap,需要在多个线程之间共享.对它的读操作远远大于写操作.所以采用了pthread的读写锁来保障并发读写时的一致性. 现在测试发现的问题是:因为读操作太多,导致 ...

  5. linux库函数pthread.h------pthread_rwlock_t读写锁说明

    读写锁 索引: 初始化一个读写锁pthread_rwlock_init 读锁定读写锁 pthread_rwlock_rdlock 非阻塞读锁定 pthread_rwlock_tryrdlock 写锁定 ...

  6. 【Linux内核】RW读写锁机制

    读写锁机制 Linux内核中读写锁的机制是一种多读单写的锁机制,它允许多个读操作同时进行,但只能有一个写操作进行.当有写操作时,所有读操作都会被阻塞,直到写操作完成. 在内核中,读写锁主要由以下两个结 ...

  7. linux线程下的读写锁

    读写锁实际是一种特殊的自旋锁,它把对共享资源的访问者划分成读者和写者,读者只对共享资源进行读访问,写者则需要对共享资源进行写操作.这种锁相对于自旋锁而言,能提高并发性,因为在多处理器系统中,它允许同时 ...

  8. 深度解析Linux读写锁逻辑

    一.Linux为何会引入读写锁? 除了mutex,在linux内核中,还有一个经常用到的睡眠锁就是rw semaphore(后文简称为rwsem),它到底和mutex有什么不同呢?为何会有rw sem ...

  9. c/c++:线程同步(互斥锁、死锁、读写锁、条件变量、生产者和消费者模型、信号量)

    目录 1. 概念 2. 互斥锁 3. 死锁 4. 读写锁 5. 条件变量 5.1 生产者和消费者模型 6. 信号量 1. 概念 线程同步: > 当有一个线程在对内存进行操作时,其他线程都不可以对 ...

最新文章

  1. 如何把 XML 文件显示为 HTML 表格
  2. linux中的3d设计软件,Linux专业画室:免费3D图形设计工具
  3. 零件库管理信息系统设计--part03:管理员登录部分设计
  4. oracle 临时表存在哪里_openGauss魔改PG?它能兼容Oracle的数据库表吗?
  5. hql刪除語句,根據參數刪除
  6. nginx根据参数转发到不同服务器_Nginx服务器之负载均衡策略
  7. linux如何安装infer
  8. 手把手教学系列——疯狂Spring Cloud教学视频
  9. SSM框架笔记06:初探Spring——采用XML配置方式
  10. 使用Xshell登录AWS的EC2云服务器和开启EC2上允许root+密码方式登录
  11. 微型计算机原理DL,微型计算机原理练习附解答.doc
  12. LeetCode:每日一题——数位成本和为目标值的最大数字
  13. js获取baseurl
  14. 1200兆路由器网速_办个100M的网,买一个1200M的路由器回家,网速真的会变快吗?...
  15. 《人人都是项目经理》-云倩读书笔记
  16. Opencv+Moviepy实现涂鸦视频和视频音轨分离合并操作。
  17. 开盘前1.5秒下单可追涨停股
  18. 物理计算机主机ip在哪,查看电脑的物理地址_查看电脑的物理ip地址
  19. 【已解决】如何让压缩率达到最大?使用lrzip工具进行文件压缩(好用)
  20. 试用HBuilder编辑H5移动开发

热门文章

  1. 用于运放的参考电压基准电路--1.24V、1.25V、2.5V
  2. php soapclient 乱码,接上一篇,php的soapclient的问题
  3. 查看Ubuntu版本号和内存等信息
  4. linux查找文件夹命令_如何在Linux中使用命令行查找文件和文件夹
  5. python循环语句中的乘法_python循环语句详细讲解
  6. CCF csp软件能力认证 第15次 第5题 管道清洁 java 100分
  7. MySQL 导入数据的几种方法
  8. 535.TinyURL 的加密与解密
  9. Maven 的打包方式
  10. 【python学习】-matplotlib绘制双坐标柱状图