一、读写锁

读写锁实际是一种特殊的自旋锁,它把对共享资源的访问者划分成读者和写者,读者只对共享资源进行读访问,写者则需要对共享资源进行写操作。

读操作可以共享,写操作是排他的,可以有多个在读(与 CPU 数相关),只能有唯一个在写,但不能同时既有读者又有写者。

如果读写锁当前没有读者,也没有写者,那么写者可以立刻获得读写锁,否则它必须自旋在那里,直到没有任何写者或读者。如果读写锁没有写者,那么读者可以立即获得该读写锁,否则读者必须自旋在那里,直到写者释放该读写锁。

具有强读者同步和强写者同步两种形式:

  • 强读者同步:当写者没有进行写操作,读者就可以访问;

  • 强写者同步:当所有写者都写完之后,才能进行读操作。

在强写者情况,读者需要最新的信息,一些事实性较高的系统可能会用到该锁,比如定票之类的。

二、特性

一次只有一个线程可以占有写模式的读写锁,但是可以有多个线程同时占有读模式的读写锁。

正因为这个特性,当读写锁是写加锁状态时,在这个锁被解锁之前, 所有试图对这个锁加锁的线程都会被阻塞。

当读写锁在读加锁状态时, 所有试图以读模式对它进行加锁的线程都可以得到访问权,但是如果线程希望以写模式对此锁进行加锁,它必须直到所有的线程释放锁。

通常, 当读写锁处于读模式锁住状态时,如果有另外线程试图以写模式加锁,读写锁通常会阻塞随后的读模式锁请求,这样可以避免读模式锁长期占用,而等待的写模式锁请求长期阻塞.

读写锁适合于对数据结构的读次数比写次数多得多的情况。因为读模式锁定时可以共享,以写模式锁住时意味着独占,所以读写锁又叫共享-独占锁。

三、小结

  1. 互斥锁与读写锁的区别

    当访问临界区资源时(访问的含义包括所有的操作:读和写),需要上互斥锁;

    当对数据(互斥锁中的临界区资源)进行读取时,需要上读取锁,当对数据进行写入时,需要上写入锁。

  2. 读写锁的优点

    对于读数据比修改数据频繁的应用,用读写锁代替互斥锁可以提高效率。因为使用互斥锁时,即使是读出数据(相当于操作临界区资源)都要上互斥锁,而采用读写锁,则可以在任一时刻允许多个读者存在,提供了更高的并发度,同时在某个写入者修改数据期间保护该数据,以免任何其它读出者或写入者的干扰。

  3. 读写锁描述:

    获取一个读写锁用于读称为共享锁,获取一个读写锁用于写称为独占锁,因此这种对于某个给定资源的共享访问也称为共享-独占上锁。

四、使用读写锁

配置读写锁的属性之后,即可初始化读写锁。以下函数用于初始化或销毁读写锁、锁定或解除锁定读写锁或尝试锁定读写锁。

  1. 初始化读写锁

    使用 pthread_rwlock_init(3C) 通过 attr 所引用的属性初始化 rwlock 所引用的读写锁。

    /**
    * @param attr 如果为 NULL,则使用缺省的读写锁属性,其作用与传递缺省读写锁属性对象的地址相同
    *
    * @return 如果成功,返回 0,否则将返回用于指明错误的错误号。 EINVAL : attr 或者 rwlock 指定的值无效
    */
    int pthread_rwlock_init(pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *attr);
    pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;

    如果 attr 为 NULL,则使用缺省的读写锁属性,其作用与传递缺省读写锁属性对象的地址相同。

    初始化读写锁之后,该锁可以使用任意次数,而无需重新初始化。成功初始化之后,读写锁的状态会变为已初始化和未锁定。如果调用 pthread_rwlock_init() 来指定已初始化的读写锁,则结果是不确定的。如果读写锁在使用之前未初始化,则结果是不确定的。

    如果缺省的读写锁属性适用,则 PTHREAD_RWLOCK_INITIALIZER 宏可初始化以静态方式分配的读写锁,其作用与通过调用pthread_rwlock_init() 并将参数 attr 指定为 NULL 进行动态初始化等效,区别在于不会执行错误检查。

    如果 pthread_rwlock_init() 失败,将不会初始化 rwlock,并且 rwlock 的内容是不确定的。

  2. 获取读写锁中的读锁

    pthread_rwlock_rdlock(3C) 可用来向 rwlock 所引用的读写锁应用读锁。

    #include <pthread.h>
    /**
    * @return 如果成功,返回 0。否则,将返回用于指明错误的错误号。 EINVAL : attr 或 rwlock 指定的值无效
    *
    */
    int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);

    如果写入器未持有读锁,并且没有任何写入器基于该锁阻塞,则调用线程会获取读锁。如果写入器未持有读锁,但有多个写入器正在等待该锁时,调用线程是否能获取该锁是不确定的。如果某个写入器持有读锁,则调用线程无法获取该锁。如果调用线程未获取读锁,则它将阻塞。调用线程必须获取该锁之后,才能从 pthread_rwlock_rdlock() 返回。如果在进行调用时,调用线程持有 rwlock 中的写锁,则结果是不确定的。

    为避免写入器资源匮乏,允许在多个实现中使写入器的优先级高于读取器。

    一个线程可以在 rwlock 中持有多个并发的读锁,该线程可以成功调用 pthread_rwlock_rdlock() n 次。该线程必须调用 pthread_rwlock_unlock() n 次才能执行匹配的解除锁定操作。

    如果针对未初始化的读写锁调用 pthread_rwlock_rdlock(),则结果是不确定的。

    线程信号处理程序可以处理传送给等待读写锁的线程的信号。从信号处理程序返回后,线程将继续等待读写锁以执行读取,就好像线程未中断一样。

  3. 读取非阻塞读写锁中的锁

    pthread_rwlock_tryrdlock(3C)应用读锁的方式与 pthread_rwlock_rdlock() 类似,区别在于如果任何线程持有 rwlock 中的写锁或者写入器基于 rwlock 阻塞,则 pthread_rwlock_tryrdlock() 函数会失败。

    #include <pthread.h>
    /**
    * @return 如果获取了用于在 rwlock 所引用的读写锁对象中执行读取的锁,返回 0。否则,返回用于指明错误的错误号
    *
    * EBUSY : 无法获取读写锁以执行读取,因为写入器持有该锁或者基于该锁已阻塞
    */
    int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
  4. 写入读写锁中的锁

    pthread_rwlock_wrlock(3C) 可用来向 rwlock 所引用的读写锁应用写锁。

    #include <pthread.h>
    /**
    * @return 如果获取了用于在 rwlock 所引用的读写锁对象中执行写入的锁,返回 0。否则,返回指明错误的错误号
    */
    int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);

    如果没有其他读取器线程或写入器线程持有读写锁 rwlock,则调用线程将获取写锁,否则,调用线程将阻塞。调用线程必须获取该锁之后,才能从 pthread_rwlock_wrlock() 调用返回。如果在进行调用时,调用线程持有读写锁(读锁或写锁),则结果是不确定的。

    为避免写入器资源匮乏,允许在多个实现中使写入器的优先级高于读取器。

    如果针对未初始化的读写锁调用 pthread_rwlock_wrlock(),则结果是不确定的。

    线程信号处理程序可以处理传送给等待读写锁以执行写入的线程的信号。从信号处理程序返回后,线程将继续等待读写锁以执行写入,就好像线程未中断一样。

  5. 写入非阻塞读写锁中的锁

    pthread_rwlock_trywrlock(3C)应用写锁的方式与 pthread_rwlock_wrlock() 类似,区别在于如果任何线程当前持有用于读取和写入的 rwlock,则pthread_rwlock_trywrlock() 函数会失败。

    #include <pthread.h>
    /**
    * @return 如果获取了用于在 rwlock 引用的读写锁对象中执行写入的锁,则返回 0,否则,返回用于指明错误的错误号
    * EBUSY : 无法为写入获取读写锁,因为已为读取或写入锁定该读写锁
    */
    int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);

    如果针对未初始化的读写锁调用 pthread_rwlock_trywrlock(),则结果是不确定的。

    线程信号处理程序可以处理传送给等待读写锁以执行写入的线程的信号。从信号处理程序返回后,线程将继续等待读写锁以执行写入,就好像线程未中断一样。

  6. 解除锁定读写锁

    pthread_rwlock_unlock(3C) 可用来释放在 rwlock 引用的读写锁对象中持有的锁。

    #include <pthread.h>
    /**
    * @return 如果成功返回 0,否则返回用于指明错误的错误号
    */
    int pthread_rwlock_unlock (pthread_rwlock_t *rwlock);

    如果调用线程未持有读写锁 rwlock,则结果是不确定的。

    如果通过调用 pthread_rwlock_unlock() 来释放读写锁对象中的读锁,并且其他读锁当前由该锁对象持有,则该对象会保持读取锁定状态。如果 pthread_rwlock_unlock() 释放了调用线程在该读写锁对象中的最后一个读锁,则调用线程不再是该对象的属主。如果 pthread_rwlock_unlock() 释放了该读写锁对象的最后一个读锁,则该读写锁对象将处于无属主、解除锁定状态。

    如果通过调用 pthread_rwlock_unlock() 释放了该读写锁对象的最后一个写锁,则该读写锁对象将处于无属主、解除锁定状态。

    如果 pthread_rwlock_unlock() 解除锁定该读写锁对象,并且多个线程正在等待获取该对象以执行写入,则通过调度策略可确定获取该对象以执行写入的线程。如果多个线程正在等待获取读写锁对象以执行读取,则通过调度策略可确定等待线程获取该对象以执行写入的顺序。如果多个线程基于 rwlock 中的读锁和写锁阻塞,则无法确定读取器和写入器谁先获得该锁。

    如果针对未初始化的读写锁调用 pthread_rwlock_unlock(),则结果是不确定的。

  7. 销毁读写锁

    pthread_rwlock_destroy(3C) 可用来销毁 rwlock 引用的读写锁对象并释放该锁使用的任何资源。

    #include <pthread.h>
    /**
    * @return 如果成功,返回 0。否则,返回用于指明错误的错误号。EINVAL : attr 或者 rwlock 指定的值无效
    */
    int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
    pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;

再次调用 pthread_rwlock_init() 重新初始化该锁之前,使用该锁所产生的影响是不确定的。实现可能会导致 pthread_rwlock_destroy() 将 rwlock 所引用的对象设置为无效值。如果在任意线程持有 rwlock 时调用 pthread_rwlock_destroy(),则结果是不确定的。尝试销毁未初始化的读写锁会产生不确定的行为。已销毁的读写锁对象可以使用 pthread_rwlock_init() 来重新初始化。销毁读写锁对象之后,如果以其他方式引用该对象,则结果是不确定的。

五、文章

百度百科

pthread_rwlock_t的更多相关文章

  1. linux使用读写锁pthread_rwlock_t

    转自:http://blog.csdn.net/onlyou930/article/details/6755593 使用读写锁 配置读写锁的属性之后,即可初始化读写锁.以下函数用于初始化或销毁读写锁. ...

  2. pthread_rwlock_t读写锁函数说明

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

  3. 读写锁的实现原理(pthread_rwlock_t)

    引言 不同的锁之间的语义是不一样的,没有一劳永逸的锁,只有更适合的锁. 如果是同一进程里的不同线程共享读写锁,那么读写锁变量的维护是在进程内部即可.如果是不同进程共享读写锁,那么读写锁变量的维护是在共 ...

  4. 读写锁pthread_rwlock_t的使用(转)

    读写锁是用来解决读者写者问题的,读操作可以共享,写操作是排他的,读可以有多个在读,写只有唯一个在写,同时写的时候不允许读. 具有强读者同步和强写者同步两种形式 强读者同步:当写者没有进行写操作,读者就 ...

  5. APUE学习之多线程编程(二):线程同步

         为了保证临界资源的安全性和可靠性,线程不得不使用锁,同一时间只允许一个或几个线程访问变量.常用的锁有互斥量,读写锁,条件变量           一.互斥量      互斥量是用pthrea ...

  6. Linux多线程学习总结

    线程是程序中完成一个独立任务的完整执行序列,即一个可调度的实体:进程相当于运行中程序的一种抽象.根据运行环境的调度者的身份,线程可分为内核线程和用户线程.内核线程,在有的系统上称为LWP(Light ...

  7. Linux多线程同步方式

    当多个线程共享相同的内存时,需要确保每个线程看到一致的数据视图,当多个线程同时去修改这片内存时,就可能出现偏差,得到与预期不符合的值.为啥需要同步,一件事情逻辑上一定是有序的,即使在并发环境下:而操作 ...

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

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

  9. 【基础】利用thrift实现一个非阻塞带有回调机制的客户端

    假设读者对thrift有一定了解. 客户端有时需要非阻塞的去发送请求,给定服务端一个请求,要求其返回一个计算结果.但是客户端不想等待服务端处理完,而是想发送完这个指令后自己去做其他事情,当结果返回时自 ...

随机推荐

  1. Samtec 5G探索之路

    序言:时代在发展,2020年5G作为元年.5G全程第五代移动通信技术(英语:5th generation mobile networks或5th generation wireless systems ...

  2. vue+element 表单封成组件(1)

    作为一名刚接触vue不到一个月的菜鸟,思想还没有从操作DOM转变为数据驱动,看vue的代码处处别扭.组里为了让我熟悉vue交给了我一个将element 表单封装成组件的练手任务.由于开发过程中遇到的表 ...

  3. 从0到1使用MyBatis

    MyBatis作为最流行的数据中间层,成为企业Java软件开发中非常重要的软件. 一.基本配置 1.首先需要导入Maven <dependency> <groupId>org. ...

  4. Python基础知识(day1)

    day1 1.编码 ASCII码 1字节8位 2^8 = 256 位 万国码 unicode 4字节32位 #浪费空间 UTF-8 对unicode进行压缩 2.注释 单行注释 score = inp ...

  5. py2.7 批量转换文件为 utf8 编码

    source insight 不支持 utf8 ,但是在 linux 上查看的时候是 utf8 编码,就会显示不正常,所以写了个 python 小脚本,可以批量转换 py2.7 #coding:utf ...

  6. 7,MapReduce基础

    目录 MapReduce基础 一.关于MapReduce 二.MapReduce的优缺点 三.MapReduce的执行流程 四.编写MapReduce程序 五.MapReduce的主要执行流程 Map ...

  7. 波兰政府在继韩国之后也增加了对 Linux 的使用

    导读 前段时间, 韩国政府起草了一项战略,准备采用基于 Linux 的开源操作系统全面取代 Windows 7,以摆脱对其的依赖. 目前,波兰的社会保险公司 ZUS( Zakład Ubezpiecz ...

  8. 智慧树刷网课python脚本

    0x00 写在前面 疫情期间肯定有很多小伙伴需要上网课,但是有些网课我们感觉十分的鸡肋,自己不感兴趣,又必须要学 所以我写了这个刷网课的小程序,一方面是锻炼自己的爬虫技术,另一方面也给同学们节约宝贵的 ...

  9. VScode 格式化代码保存时使用ESlint修复代码

    前言 eslint  vs code 新买的电脑啊啊西 装VScode 配置格式化代码保存时使用ESlint修复代码头快炸了,不建议初学者用,太费时间了: 终于搞定---再也不要担心缩进,函数(名)和 ...

  10. 浅析Redis分布式锁---从自己实现到Redisson的实现

    当我们在单机情况下,遇到并发问题,可以使用juc包下的lock锁,或者synchronized关键字来加锁.但是这俩都是JVM级别的锁,如果跨了JVM这两个锁就不能控制并发问题了,也就是说在分布式集群 ...