读写锁和互斥量(互斥锁)很类似,是另一种线程同步机制,但不属于POSIX标准,可以用来同步同一进程中的各个线程。当然如果一个读写锁存放在多个进程共享的某个内存区中,那么还可以用来进行进程间的同步,

和互斥量不同的是:互斥量会把试图进入已保护的临界区的线程都阻塞;然而读写锁会视当前进入临界区的线程和请求进入临界区的线程的属性来判断是否允许线程进入。

相对互斥量只有加锁和不加锁两种状态,读写锁有三种状态:读模式下的加锁,写模式下的加锁,不加锁

读写锁的使用规则:

  • 只要没有写模式下的加锁,任意线程都可以进行读模式下的加锁;
  • 只有读写锁处于不加锁状态时,才能进行写模式下的加锁;

读写锁也称为共享-独占(shared-exclusive)锁,当读写锁以读模式加锁时,它是以共享模式锁住,当以写模式加锁时,它是以独占模式锁住。读写锁非常适合读数据的频率远大于写数据的频率从的应用中。这样可以在任何时刻运行多个读线程并发的执行,给程序带来了更高的并发度。

需要提到的是:读写锁到目前为止仍然不是属于POSIX标准,本文讨论的读写锁函数都是有Open Group定义的的。例如下面是在我机器上,编译器是gcc version 4.4.6,关于读写锁的定义是包含在预处理命令中的:

  1. #if defined __USE_UNIX98 || defined __USE_XOPEN2K
  2. ... 读写锁相关函数声明...
  3. #endif

1读写锁的初始化和销毁

  1. /* Initialize read-write lock  */
  2. int pthread_rwlock_init (pthread_rwlock_t *__restrict __rwlock,
  3. __const pthread_rwlockattr_t *__restrict __attr);
  4. /* Destroy read-write lock */
  5. extern int pthread_rwlock_destroy (pthread_rwlock_t *__rwlock);
  6. 返回值:成功返回0,否则返回错误代码

上面两个函数分别由于读写锁的初始化和销毁。和互斥量,条件变量一样,如果读写锁是静态分配的,可以通过常量进行初始化,如下:

  1. pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;

也可以通过pthread_rwlock_init()进行初始化。对于动态分配的读写锁由于不能直接赋值进行初始化,只能通过这种方式进行初始化。pthread_rwlock_init()第二个参数是读写锁的属性,如果采用默认属性,可以传入空指针NULL。

那么当不在需要使用时及释放(自动或者手动)读写锁占用的内存之前,需要调用pthread_rwlock_destroy()进行销毁读写锁占用的资源。

2读写锁的属性设置

  1. /* 初始化读写锁属性对象 */
  2. int pthread_rwlockattr_init (pthread_rwlockattr_t *__attr);
  3. /* 销毁读写锁属性对象 */
  4. int pthread_rwlockattr_destroy (pthread_rwlockattr_t *__attr);
  5. /* 获取读写锁属性对象在进程间共享与否的标识*/
  6. int pthread_rwlockattr_getpshared (__const pthread_rwlockattr_t * __restrict __attr,
  7. int *__restrict __pshared);
  8. /* 设置读写锁属性对象,标识在进程间共享与否  */
  9. int pthread_rwlockattr_setpshared (pthread_rwlockattr_t *__attr, int __pshared);
  10. 返回值:成功返回0,否则返回错误代码

这个属性设置和互斥量的基本一样,具体可以参考互斥量的设置互斥量的属性设置

3读写锁的使用

  1. /* 读模式下加锁  */
  2. int pthread_rwlock_rdlock (pthread_rwlock_t *__rwlock);
  3. /* 非阻塞的读模式下加锁  */
  4. int pthread_rwlock_tryrdlock (pthread_rwlock_t *__rwlock);
  5. # ifdef __USE_XOPEN2K
  6. /*  限时等待的读模式加锁 */
  7. int pthread_rwlock_timedrdlock (pthread_rwlock_t *__restrict __rwlock,
  8. __const struct timespec *__restrict __abstime);
  9. # endif
  10. /* 写模式下加锁  */
  11. int pthread_rwlock_wrlock (pthread_rwlock_t *__rwlock);
  12. /* 非阻塞的写模式下加锁 */
  13. int pthread_rwlock_trywrlock (pthread_rwlock_t *__rwlock);
  14. # ifdef __USE_XOPEN2K
  15. /* 限时等待的写模式加锁 */
  16. int pthread_rwlock_timedwrlock (pthread_rwlock_t *__restrict __rwlock,
  17. __const struct timespec *__restrict __abstime);
  18. # endif
  19. /* 解锁 */
  20. int pthread_rwlock_unlock (pthread_rwlock_t *__rwlock);
  21. 返回值:成功返回0,否则返回错误代码

(1)pthread_rwlock_rdlock()系列函数

pthread_rwlock_rdlock()用于以读模式即共享模式获取读写锁,如果读写锁已经被某个线程以写模式占用,那么调用线程就被阻塞。在实现读写锁的时候可以对共享模式下锁的数量进行限制(目前不知如何限制)。

pthread_rwlock_tryrdlock()和pthread_rwlock_rdlock()的唯一区别就是,在无法获取读写锁的时候,调用线程不会阻塞,会立即返回,并返回错误代码EBUSY。

pthread_rwlock_timedrdlock()是限时等待读模式加锁,时间参数struct timespec * __restrict __abstime也是绝对时间,和条件变量的pthread_cond_timedwait()使用基本一致,具体可以参考pthread_cond_timedwait()3条件变量的使用

(2)pthread_rwlock_wrlock()系列函数

pthread_rwlock_wrlock()用于写模式即独占模式获取读写锁,如果读写锁已经被其他线程占用,不论是以共享模式还是独占模式占用,调用线程都会进入阻塞状态。

pthread_rwlock_trywrlock()在无法获取读写锁的时候,调用线程不会进入睡眠,会立即返回,并返回错误代码EBUSY。

pthread_rwlock_timedwrlock()是限时等待写模式加锁,也和条件变量的pthread_cond_timedwait()使用基本一致,具体可以参考pthread_cond_timedwait()3条件变量的使用

(3)pthread_rwlock_unlock()

无论以共享模式还是独占模式获得的读写锁,都可以通过调用pthread_rwlock_unlock()函数进行释放该读写锁。

下面是测试代码:

  1. #include <iostream>
  2. #include <cstdlib>
  3. #include <unistd.h>
  4. #include <pthread.h>
  5. using namespace std;
  6. struct{
  7. pthread_rwlock_t rwlock;
  8. int product;
  9. }sharedData = {PTHREAD_RWLOCK_INITIALIZER, 0};
  10. void * produce(void *ptr)
  11. {
  12. for (int i = 0; i < 5; ++i)
  13. {
  14. pthread_rwlock_wrlock(&sharedData.rwlock);
  15. sharedData.product = i;
  16. pthread_rwlock_unlock(&sharedData.rwlock);
  17. sleep(1);
  18. }
  19. }
  20. void * consume1(void *ptr)
  21. {
  22. for (int i = 0; i < 5;)
  23. {
  24. pthread_rwlock_rdlock(&sharedData.rwlock);
  25. cout<<"consume1:"<<sharedData.product<<endl;
  26. pthread_rwlock_unlock(&sharedData.rwlock);
  27. ++i;
  28. sleep(1);
  29. }
  30. }
  31. void * consume2(void *ptr)
  32. {
  33. for (int i = 0; i < 5;)
  34. {
  35. pthread_rwlock_rdlock(&sharedData.rwlock);
  36. cout<<"consume2:"<<sharedData.product<<endl;
  37. pthread_rwlock_unlock(&sharedData.rwlock);
  38. ++i;
  39. sleep(1);
  40. }
  41. }
  42. int main()
  43. {
  44. pthread_t tid1, tid2, tid3;
  45. pthread_create(&tid1, NULL, produce, NULL);
  46. pthread_create(&tid2, NULL, consume1, NULL);
  47. pthread_create(&tid3, NULL, consume2, NULL);
  48. void *retVal;
  49. pthread_join(tid1, &retVal);
  50. pthread_join(tid2, &retVal);
  51. pthread_join(tid3, &retVal);
  52. return 0;
  53. }

测试结果如下:

  1. consume1:0
  2. consume2:0
  3. consume2:0
  4. consume1:1
  5. consume2:1
  6. consume1:2
  7. consume2:2
  8. consume1:3
  9. consume2:3
  10. consume1:4

如果把consume1的解锁注释掉,如下:

  1. void * consume1(void *ptr)
  2. {
  3. for (int i = 0; i < 5;)
  4. {
  5. pthread_rwlock_rdlock(&sharedData.rwlock);
  6. cout<<"consume1:"<<sharedData.product<<endl;
  7. //pthread_rwlock_unlock(&sharedData.rwlock);
  8. ++i;
  9. sleep(1);
  10. }
  11. }

程序的执行结果如下:

  1. consume1:0
  2. consume2:0
  3. consume2:0
  4. consume1:0
  5. consume2:0
  6. consume1:0
  7. consume2:0
  8. consume1:0
  9. consume2:0
  10. consume1:0

从执行结果可以看出Linux 2.6.18提供的读写锁函数是优先考虑等待读模式占用锁的线程,这种实现的一个很大缺陷就是出现写入线程饿死的情况。

Jun 26, 2013 AM 00:08 @Library

原文:http://blog.csdn.net/anonymalias/article/details/9174595

Linux线程同步之读写锁(rwlock)的更多相关文章

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

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

  2. UNIX环境高级编程——线程同步之读写锁以及属性

    读写锁和互斥量(互斥锁)很类似,是另一种线程同步机制,但不属于POSIX标准,可以用来同步同一进程中的各个线程.当然如果一个读写锁存放在多个进程共享的某个内存区中,那么还可以用来进行进程间的同步, 互 ...

  3. 线程同步——用户模式下线程同步——Slim读写锁实现线程同步

    //Slim读/写锁实现线程同步 SRWlock 的目的和关键段相同:对同一资源进行保护,不让其它线程访问. 但是,与关键段不同的是,SRWlock允许我们区分哪些想要读取资源的线程(读取者线程) 和 ...

  4. Linux系统编程 —读写锁rwlock

    读写锁是另一种实现线程间同步的方式.与互斥量类似,但读写锁将操作分为读.写两种方式,可以多个线程同时占用读模式的读写锁,这样使得读写锁具有更高的并行性. 读写锁的特性为:写独占,读共享:写锁优先级高. ...

  5. linux c编程:读写锁

    什么是读写锁读写锁其实还是一种锁,是给一段临界区代码加锁,但是此加锁是在进行写操作的时候才会互斥,而在进行读的时候是可以共享的进行访问临界区的 为什么需要读写锁有时候,在多线程中,有一些公共数据修改的 ...

  6. 嵌入式 Linux线程同步读写锁rwlock示例

    读写锁比mutex有更高的适用性,可以多个线程同时占用读模式的读写锁,但是只能一个线程占用写模式的读写锁.1. 当读写锁是写加锁状态时,在这个锁被解锁之前,所有试图对这个锁加锁的线程都会被阻塞:2. ...

  7. linux线程间同步(1)读写锁

    读写锁比mutex有更高的适用性,能够多个线程同一时候占用读模式的读写锁.可是仅仅能一个线程占用写模式的读写锁. 1. 当读写锁是写加锁状态时,在这个锁被解锁之前,全部试图对这个锁加锁的线程都会被堵塞 ...

  8. Linux线程同步

    1. 线程同步: 当多个控制线程共享相同的内存时,需要确保每个线程看到一致的数据视图.当某个线程可以修改变量,而其他线程也可以读取或者修改这个变量的时候,就需要对这些线程进行同步,以确保他们在访问变量 ...

  9. linux kernel RCU 以及读写锁

    信号量有一个很明显的缺点,没有区分临界区的读写属性,读写锁允许多个线程进程并发的访问临界区,但是写访问只限于一个线程,在多处理器系统中允许多个读者访问共享资源,但是写者有排他性,读写锁的特性如下:允许 ...

随机推荐

  1. 如何使用原生的Feign

    什么是Feign Feign 是由 Netflix 团队开发的一款基于 Java 实现的 HTTP client,借鉴了 Retrofi. JAXRS-2.0.WebSocket 等类库.通过 Fei ...

  2. 【译】Go:程序如何恢复?

    原文:https://medium.com/a-journey-with-go/go-how-does-a-program-recover-fbbbf27cc31e ​ 当程序不能正确处理错误时, 会 ...

  3. git分支案例

  4. 深入理解 SynchronizationContext

    深入理解 SynchronizationContext 目录 深入理解 SynchronizationContext SynchronizationContext(后续以SC简称) 是什么? 1.1 ...

  5. Game游戏分析

    1.鲁棒图分析 2.系统上下文及交互方式 3.用例 4.逻辑拓扑图 5.物理拓扑图 6.时序图 7.状态图 8.物理数据模型 9.类图 10.技术选型 11.框架搭建 12.工具及通用服务 13.架构 ...

  6. 26 bash shell中的信号

    当没有任何捕获时,一个交互式 Bash Shell 会忽略 SIGTERM(发送到进程的 TERM 信号用于要求进程终止) 和 SIGQUIT(当用户要求进程执行 core dump 时,QUIT 信 ...

  7. robotframework使用过程中的一些总结

    p.p1 { margin: 0; font: 20px "Helvetica Neue"; color: rgba(53, 53, 53, 1) } p.p2 { margin: ...

  8. ctf实验吧Once More

    题目链接:http://ctf5.shiyanbar.com/web/more.php 思路分析:显然是后台逻辑代码. 1.ereg函数有漏洞,可以使用%00截断,这个就做笔记了好吧.这个函数大致意思 ...

  9. 深入理解Java多线程——线程池

    目录 为什么需要线程池 定义 ThreadPoolExecutor 工作队列workQueue 不同的线程池 Executor 线程池的工作原理 线程池生命周期 线程池增长策略 线程池大小的设置 线程 ...

  10. kong插件Prometheus+grafana图形化展示

    目录 1. 准备工作 3. 为kong添加 prometheus插件 4. 打开kong的metrics支持 4. 配置prometheus.yml添加kong提供的数据信息 5. 在 Grafana ...