Linux多线程实践(6) --Posix读写锁解决读者写者问题
Posix读写锁
int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock, const pthread_rwlockattr_t *restrict attr); int pthread_rwlock_destroy(pthread_rwlock_t *rwlock); int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock); int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock); int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
读写锁与互斥量类似, 不过读写锁允许更高的并行性. 读写锁用于读称为共享锁, 读写锁用于写称为排它锁;
读写锁规则:
只要没有线程持有给定的读写锁用于写, 那么任意数目的线程可以持有读写锁用于读;
仅当没有线程持有某个给定的读写锁用于读或用于写时, 才能分配读写锁用于写;
Posix自旋锁
int pthread_spin_destroy(pthread_spinlock_t *lock); int pthread_spin_init(pthread_spinlock_t *lock, int pshared); int pthread_spin_lock(pthread_spinlock_t *lock); int pthread_spin_trylock(pthread_spinlock_t *lock); int pthread_spin_unlock(pthread_spinlock_t *lock);
自旋锁类似于互斥锁, 它的性能比互斥锁更高;
自旋锁与互斥锁很重要的一个区别在于: 线程在申请自旋锁的时候, 线程并不会挂起, 它总是处于忙等待的状态(一直在自旋, CPU处于空耗的状态);
自旋锁可用于以下情况:锁被持有的时间短, 而且线程并不希望在重新调度上花费太多的成本.
自旋锁通常作为底层原语用于实现其他类型的锁: 比如有些互斥锁的实现在试图获取互斥量的时候会自旋一小段时间, 只有在自旋计数到达某一阈值的时候才会休眠; 因此, 很多互斥量的实现非常搞笑, 以至于应用程序采用互斥锁的性能与曾经采用过自旋锁的性能基本上是相同的.
因此, 自旋锁只在某些特定的情况下有用, 比如在用户层, 自旋锁并不是非常有用, 除非运行在不允许抢占的实时调度类中.
读者写者问题
问题描述
一个数据对象可以为多个并发进程所共享。其中有的进程可能只需要读共享对象的内容,而其他进程可能要更新共享对象的内容。
读者:只对读感兴趣的进程;
写者:其他进程(只写,或既读又写);
规则
允许多个读者同时读取数据;
只有一个写者可以写数据;
写者在写时读者不能读,反之亦然。
/** 实现1: 运用读写锁解决”读者写者问题” 解题思路: 将需要读写的文件实现为一个字符串; 读者进程: 一次可以将该字符串全部读出, 然后打印读取信息 写者进程: 一次只能修改一个字符(该字符从A~Z循环写入), 修改之后打印写入信息 **/ //读写锁 pthread_rwlock_t rwlock; const unsigned READERCOUNT = 2; //读者数 const unsigned WRITERCONUT = 5; //写者数 const int PAPERSIZE = 32; //文件长度 char paper[PAPERSIZE+1]; //文件 unsigned short int write_index = 0; //写者需要写入的位置 char ch = 'A'; //写者需要写入的字母 pthread_t thread[READERCOUNT+WRITERCONUT]; //读者+写者线程 //读者线程 void *reader(void *args) { int number = *(int *)args; delete (int *)args; while (true) { //获取共享锁 pthread_rwlock_rdlock(&rwlock); //开始读 printf("## reader %d was reading...\n", number); printf("text: %s\n", paper); printf(" reader %d end reading...\n", number); //解锁共享锁 pthread_rwlock_unlock(&rwlock); sleep(1); } pthread_exit(NULL); } //写者线程 void *writer(void *args) { int number = *(int *)args; delete (int *)args; while (true) { //获取写锁 pthread_rwlock_wrlock(&rwlock); //开始写 printf("++ writer %d was writing...\n", number); paper[write_index] = ch; write_index = (write_index+1)%PAPERSIZE; ch = ch+1; if (ch > 'Z') ch = 'A'; printf(" writer %d end writing...\n", number); //释放写锁 pthread_rwlock_unlock(&rwlock); sleep(1); } pthread_exit(NULL); } int main() { memset(paper, 0, sizeof(paper)); pthread_rwlock_init(&rwlock, NULL); for (unsigned int i = 0; i < READERCOUNT; ++i) pthread_create(&thread[i], NULL, reader, new int(i)); for (unsigned int i = 0; i < WRITERCONUT; ++i) pthread_create(&thread[READERCOUNT+i], NULL, writer, new int(i)); for (unsigned int i = 0; i < READERCOUNT+WRITERCONUT; ++i) pthread_join(thread[i], NULL); pthread_rwlock_destroy(&rwlock); }
/** 实现2: 运用Posix信号量使用”读者优先”策略解决”读者写者问题” 解题思路: 如果新读者到: ①无读者、写者,新读者可以读; ②有写者等待,但有其它读者正在读,则新读者也可以读; ③有写者写,新读者等待。 如果新写者到: ①无读者,新写者可以写; ②有读者,新写者等待; ③有其它写者,新写者等待。 **/ // 需要用两个互斥量实现 pthread_mutex_t rmutex; pthread_mutex_t wmutex; const unsigned READERCOUNT = 5; //读者数 const unsigned WRITERCONUT = 5; //写者数 const int PAPERSIZE = 32; //文件长度 char paper[PAPERSIZE+1]; //文件 unsigned short int write_index = 0; //写者需要写入的位置 char ch = 'A'; //写者需要写入的字母 pthread_t thread[READERCOUNT+WRITERCONUT]; //读者+写者线程 int nReader = 0; //读者线程 void *reader(void *args) { int number = *(int *)args; delete (int *)args; while (true) { pthread_mutex_lock(&rmutex); //如果是第一个读者, 则锁定wmutex if (nReader == 0) pthread_mutex_lock(&wmutex); ++ nReader; pthread_mutex_unlock(&rmutex); //开始读 printf("## reader %d was reading...\n", number); printf("text: %s\n", paper); printf(" reader %d end reading...\n\n", number); pthread_mutex_lock(&rmutex); -- nReader; //如果是最后一个读者, 则解锁wmutex if (nReader == 0) pthread_mutex_unlock(&wmutex); pthread_mutex_unlock(&rmutex); sleep(1); } pthread_exit(NULL); } //写者线程 void *writer(void *args) { int number = *(int *)args; delete (int *)args; while (true) { //获取写锁 pthread_mutex_lock(&wmutex); //开始写 printf("++ writer %d was writing...\n", number); paper[write_index] = ch; write_index = (write_index+1)%PAPERSIZE; ch = ch+1; if (ch > 'Z') ch = 'A'; printf(" writer %d end writing...\n\n", number); //释放写锁 pthread_mutex_unlock(&wmutex); sleep(1); } pthread_exit(NULL); } int main() { memset(paper, 0, sizeof(paper)); pthread_mutex_init(&rmutex, NULL); pthread_mutex_init(&wmutex, NULL); for (unsigned int i = 0; i < READERCOUNT; ++i) pthread_create(&thread[i], NULL, reader, new int(i)); for (unsigned int i = 0; i < WRITERCONUT; ++i) pthread_create(&thread[READERCOUNT+i], NULL, writer, new int(i)); for (unsigned int i = 0; i < READERCOUNT+WRITERCONUT; ++i) pthread_join(thread[i], NULL); pthread_mutex_destroy(&rmutex); pthread_mutex_destroy(&wmutex); }
“读者优先”思想小结: 读者优先的设计思想是读进程只要看到有其它读进程正在读,就可以继续进行读;写进程必须等待所有读进程都不读时才能写,即使写进程可能比一些读进程更早提出申请。该算法只要还有一个读者在活动,就允许后续的读者进来,该策略的结果是,如果有一个稳定的读者流存在,那么这些读者将在到达后被允许进入。而写者就始终被挂起,直到没有读者为止.
Linux多线程实践(6) --Posix读写锁解决读者写者问题的更多相关文章
- Linux多线程实践(8) --Posix条件变量解决生产者消费者问题
Posix条件变量 int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr); int pthread_co ...
- Linux多线程实践(5) --Posix信号量与互斥量解决生产者消费者问题
Posix信号量 Posix 信号量 有名信号量 无名信号量 sem_open sem_init sem_close sem_destroy sem_unlink sem_wait sem_post ...
- C#使用读写锁解决多线程并发写入文件时线程同步的问题
读写锁是以 ReaderWriterLockSlim 对象作为锁管理资源的,不同的 ReaderWriterLockSlim 对象中锁定同一个文件也会被视为不同的锁进行管理,这种差异可能会再次导致文件 ...
- FileShare文件读写锁解决“文件XXX正由另一进程使用,因此该进程无法访问此文件”(转)
开发过程中,我们往往需要大量与文件交互,读文件,写文件已成家常便饭,本地运行完美,但一上到投产环境,往往会出现很多令人措手不及的意外,或开发中的烦恼,因此,我对普通的C#文件操作做了一次总结,问题大部 ...
- 四十、Linux 线程——互斥锁和读写锁
40.1 互斥锁 40.1.1 介绍 互斥锁(mutex)是一种简单的加锁的方法来控制对共享资源的访问. 在同一时刻只能有一个线程掌握某个互斥锁,拥有上锁状态的线程能够对共享资源进行访问. 若其他线程 ...
- 22、Java并发性和多线程-Java中的读/写锁
以下内容转自http://ifeve.com/read-write-locks/: 相比Java中的锁(Locks in Java)里Lock实现,读写锁更复杂一些.假设你的程序中涉及到对一些共享资源 ...
- linux线程间同步(1)读写锁
读写锁比mutex有更高的适用性,能够多个线程同一时候占用读模式的读写锁.可是仅仅能一个线程占用写模式的读写锁. 1. 当读写锁是写加锁状态时,在这个锁被解锁之前,全部试图对这个锁加锁的线程都会被堵塞 ...
- Linux IPC实践(10) --Posix共享内存
1. 创建/获取一个共享内存 #include <sys/mman.h> #include <sys/stat.h> /* For mode constants */ #inc ...
- Linux多线程实践(六)使用Posix条件变量解决生产者消费者问题
前面的一片文章我们已经讲过使用信号量解决生产者消费者问题.那么什么情况下我们须要引入条件变量呢? 这里借用 http://www.cnblogs.com/ngnetboy/p/3521547.htm ...
随机推荐
- Java锁Synchronized对象锁和类锁区别
java的内置锁:每个java对象都可以用做一个实现同步的锁,这些锁成为内置锁.线程进入同步代码块或方法的时候会自动获得该锁,在退出同步代码块或方法时会释放该锁.获得内置锁的唯一途径就是进入这个锁的保 ...
- C# 获取当前屏幕DPI
1.通过Graphics类获取 Graphics currentGraphics = Graphics.FromHwnd(new WindowInteropHelper(mainWindow).Han ...
- 反射 类的加载 Schema DOM 解析方式和解析器 命名空间
Day15 反射 1.1 类的加载 当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化三步来实现对这个类进行初始化. l 加载 就是指将class文件读入内存,并为之创建 ...
- DotnetSpider (一) 架构的理解、应用、搭建
第一次写博客,比较浅显,欢迎大牛们指点一二,不胜感激. ** 温馨提示:如需转载本文,请注明内容出处.** 本文连接:http://www.cnblogs.com/grom/p/8931650 ...
- 关于Application_End 与 Application_Start事件触发情况的测试(待续)
测试项目搭建 定义一个简单的Mvc项目,有如下文件: (1) public class Startup { public void Configuration(IAppBuilder app) { a ...
- java里String类为何被设计为final
前些天面试遇到一个非常难的关于String的问题,"String为何被设计为不可变的"?类似的问题也有"String为何被设计为final?"个人认为还是前面一 ...
- Hadoop — MapReduce原理解析
1. 概述 Mapreduce是一个分布式运算程序的编程框架,是用户开发"基于hadoop的数据分析应用"的核心框架: Mapreduce核心功能是将用户编写的业务逻辑代码和自带默 ...
- C语言中file文件指针概念及其操作 (转载)
文件 文件的基本概念 所谓"文件"是指一组相关数据的有序集合. 这个数据集有一个名称,叫做文件名.实际上在前面的各章中我们已经多次使用了文件,例如源程序文件.目标文件.可执行文件. ...
- django之数据库orm
一.数据库的配置 1 django默认支持sqlite,mysql, oracle,postgresql数据库. <1>sqlite django默认使用sqlite的数据库,默认自带sq ...
- 实验与作业(Python)-文件操作
1.CSV文件的处理 下载-身份证号文件 导入: 读入"身份证号.txt",然后打印出来.注意:是否多打了一行,为什么? 读入"身份证号.txt",然后存储到& ...