OS: 读者写者问题(写者优先+LINUX+多线程+互斥量+代码)(转)
一. 引子
二. 互斥量与信号量
三. 读者优先
1. 读者
1) 写者写时,不可读
2) 有别的读者正在读,可读
2. 写者
1) 有读者正在读,不可写
2) 有写者正在写,不可写
3) 无读者正在读,无写者正在写,可写
四. 写者优先
1. 读者特点
1) 有写者正在写或者等待写,须等到没有写者才能读
2) 没有写者,可以读
2. 写者特点
1) 写者与写者互斥。当其它写者正在写时,其它写者不能写。
2) 写者与读者互斥。之前只有读者在读,当写者出现时,必须等到之前的读者都读完才能写。这尊重了之前读者的意愿。
3) 写者可以有条件地插读者的队。当前有写者正写,有读者在等,这时来了新写者,新写者可以在那些读者之前执行。这不尊重
3. 写者实现
1 while(1) 2 { 3 pthread_mutex_lock(&writeLock); 4 {//临界区,限制只有一个写者修改数据 5 write(); 6 } 7 pthread_mutex_unlock(&writeLock); 8 }
考虑到写者对读者的影响是:当任何写者想写时,读者都必须被阻塞;并且,写者阻塞了读者并停止阻塞之前,后续的任何写者都会优先于读者执行。这就如同有一个写者队列,当第一个写者入队时,读者完全被阻塞,直到最后一个写者离开队列。
1 while(1) 2 { 3 pthread_mutex_lock(&accessWriterCnt); 4 {//临界区,希望修改 writerCnt,独占 writerCnt 5 writerCnt++; 6 if(writerCnt == 1){ 7 //读者与写者互斥;使用readerLock来描述; 8 pthread_mutex_lock(&readerLock); 9 } 10 } 11 pthread_mutex_unlock(&accessWriterCnt); 12 13 14 pthread_mutex_lock(&writeLock); 15 {//临界区,限制只有一个写者修改数据 16 write(); 17 } 18 pthread_mutex_unlock(&writeLock); 19 20 pthread_mutex_lock(&accessWriterCnt); 21 {//临界区,希望修改 writerCnt,独占 writerCnt 22 writerCnt--; 23 if(writerCnt == 0){ 24 //阻止后续的读者加入待读队列 25 pthread_mutex_unlock(&readerLock); 26 } 27 } 28 pthread_mutex_unlock(&accessWriterCnt); 29 sleep(W_SLEEP); 30 }
4. 读者的实现
1 while(1) 2 { 3 pthread_mutex_lock(&readerLock); 4 {//临界区 5 read(); 6 } 7 pthread_mutex_unlock(&readerLock); 8 }
在上面的实现中,很明显的问题是,当一个读者执行 read() 的同时,不可能有另外的读者进入临界区并执行 read() 函数了。因此,必须确保在执行 read() 函数之前对readerLock解锁。代码类似于:
1 while(1) 2 { 3 pthread_mutex_lock(&readerLock); 4 pthread_mutex_unlock(&readerLock); 5 read(); 6 }
于是乎,现在我们可以注意到,假如写者率先锁定了 readerLock , 那么后续的读者被阻塞在 pthread_mutex_lock(&readerLock); 了,这点很正确。在写者最终释放 readerLock 之前,可能有成千上万的读者都被阻塞在 readerLock 上, readerLock 释放之后,这些读者会竞争这 readerLock,嗯,这没什么问题。问题在于,此后,将可能有新的写者有写需求并希望获得这把锁(参照一下写者代码吧),那么,此时,写者不得不和成千上万的读者一起竞争 readerLock,由于将占有 readerLock 的人使随机的,写者在数量上不占优势,将进入饥饿状态。因此,这种实现无法满足“写者优先”的需求。
1 while(1) 2 { 3 pthread_mutex_lock(&outerLock);//成千上万的读者被锁在这里 4 pthread_mutex_lock(&readerLock);//只被一个读者占有 5 6 pthread_mutex_unlock(&readerLock);//释放时,写者将优先获得readerLock 7 pthread_mutex_unlock(&outerLock); 8 read(); 9 }
为了防止执行 read() 的同时,写者正在执行 write(),所以,有必要在读者的代码中,等待 write() 函数执行完,注意到写者的代码中,使用writeLock 互斥量来保护数据:
1 pthread_mutex_lock(&writeLock); 2 {//临界区,限制只有一个写者修改数据 3 write(); 4 } 5 pthread_mutex_unlock(&writeLock);
因而,读者也可以通过对 writeLock 加锁以保证读的时候没有同时写,但是下面这种做法又导致读者无法并发:
1 while(1) 2 { 3 pthread_mutex_lock(&outerLock);//成千上万的读者被锁在这里 4 pthread_mutex_lock(&readerLock);//只被一个读者占有 5 6 pthread_mutex_unlock(&readerLock);//释放时,写者将优先获得readerLock 7 pthread_mutex_unlock(&outerLock); 8 9 pthread_mutex_lock(&writeLock);//每个执行到这里的读者都企图对 writeLock 加锁 10 {//临界区 11 read();//同一时刻只能有一个读者在这里,无法实现读并发 12 } 13 pthread_mutex_unlock(&writeLock); 14 }
我们需要更合理地安排 lock(&writeLock) 的位置,并且需要避免每个读者都对 writeLock 加锁,即,只让某个读者对 writeLock 加锁,同时还保证读的时候,没有写者在写 。
1 pthread_mutex_lock(&accessReaderCnt); 2 {//临界区 3 readerCnt++; 4 if(readerCnt == 1){//第一个并发读线程 5 pthread_mutex_lock(&writeLock);//在第一个并发读者这里开始禁止写者执行写操作 6 } 7 } 8 pthread_mutex_unlock(&accessReaderCnt);
1 pthread_mutex_lock(&accessReaderCnt); 2 {//临界区 3 readerCnt--; 4 if(readerCnt == 0){//最后一个并发读线程 5 pthread_mutex_unlock(&writeLock);//在最后一个并发读者read()结束后写者可以执行写操作 6 } 7 } 8 pthread_mutex_unlock(&accessReaderCnt);
1 while(1) 2 { 3 4 //代码段1 可能的位置 0 5 pthread_mutex_lock(&outerLock);//假如写者锁定了readerLock,那么成千上万的读者被锁在这里 6 //代码段1 可能的位置 1 7 pthread_mutex_lock(&readerLock);//只被一个读者占有 8 //代码段1 可能的位置 2 9 pthread_mutex_unlock(&readerLock);//释放时,写者将优先获得readerLock 10 //代码段1 可能的位置 3 11 pthread_mutex_unlock(&outerLock); 12 //代码段1 可能的位置 4 13 14 read(); 15 pthread_mutex_lock(&accessReaderCnt);//代码段2 16 {//临界区 17 readerCnt--; 18 if(readerCnt == 0){ 19 pthread_mutex_unlock(&writeLock);//在最后一个并发读者读完这里开始禁止写者执行写操作 20 } 21 } 22 pthread_mutex_unlock(&accessReaderCnt); 23 }
经典的做法是把代码段1放到位置2,如果是这样,那么代码如下,我故意用了很多块作用域(用花括号{}表示)来凸显临界区:
1 while(1) 2 { 3 pthread_mutex_lock(&outerLock);//假如写者锁定了readerLock,那么成千上万的读者被锁在这里 4 {//临界区 5 pthread_mutex_lock(&readerLock);//只被一个读者占有 6 {//临界区 7 pthread_mutex_lock(&accessReaderCnt);//代码段 1 8 {//临界区 9 readerCnt++; 10 if(readerCnt == 1){ 11 pthread_mutex_lock(&writeLock); 12 } 13 } 14 pthread_mutex_unlock(&accessReaderCnt); 15 } 16 pthread_mutex_unlock(&readerLock);//释放时,写者将优先获得readerLock 17 } 18 pthread_mutex_unlock(&outerLock); 19 20 read(); 21 22 pthread_mutex_lock(&accessReaderCnt);//代码段2 23 {//临界区 24 readerCnt--; 25 if(readerCnt == 0){ 26 pthread_mutex_unlock(&writeLock);//在最后一个并发读者读完这里开始禁止写者执行写操作 27 } 28 } 29 pthread_mutex_unlock(&accessReaderCnt); 30 }
如此布局的意义在于,必须放在 lock(&readerLock) 和 unlock(&readerLock) 之间锁定 writeLock 。否则,一旦读者释放了 readerLock ,后续的写进程(如果有)可能会率先获得 writeLock 锁,从而导致比读者后发生的写者先执行。
提示:读者在lock(&readerLock) 和 unlock(&readerLock) 之间锁定 writeLock 是绝对不会阻塞的。因为读者已经占据了 readerLock 互斥量,这意味着,即便有写者,它也不可能已经占据了 writeLock 锁。
5. 运行结果
write 383
write 886
read 886
write 777
write 915
read 915
write 793
write 335
read 335
没出现什么乱子
嗯,该睡了,不喜熬夜。
附录.
1. 读者优先代码
1 /* 2 * 多线程,读者优先 3 */ 4 5 #include "stdio.h" 6 #include <stdlib.h> 7 #include <pthread.h> 8 9 10 #define N_WRITER 3 //写者数目 11 #define N_READER 5 //读者数目 12 #define W_SLEEP 1 //控制写频率 13 #define R_SLEEP 1 //控制读频率 14 15 16 pthread_t wid[N_WRITER],rid[N_READER]; 17 const int MAX_RAND = 1000;//产生的最大随机数 18 pthread_mutex_t writeLock = PTHREAD_MUTEX_INITIALIZER;//同一时间只能一个人写文件,互斥 19 pthread_mutex_t accessReaderCnt = PTHREAD_MUTEX_INITIALIZER;//同一时间只能有一个人访问 readerCnt 20 int data = 0; 21 int readerCnt = 0; 22 void write() 23 { 24 int rd = rand(); 25 printf("write %d\n",rd); 26 data = rd; 27 } 28 void read() 29 { 30 printf("read %d\n",data); 31 } 32 void * writer(void * in) 33 { 34 while(1) 35 { 36 pthread_mutex_lock(&writeLock); 37 write(); 38 pthread_mutex_unlock(&writeLock); 39 sleep(W_SLEEP); 40 } 41 pthread_exit((void *) 0); 42 } 43 44 void * reader (void * in) 45 { 46 while(1) 47 { 48 pthread_mutex_lock(&accessReaderCnt); 49 readerCnt++; 50 if(readerCnt == 1){ 51 pthread_mutex_lock(&writeLock); 52 } 53 pthread_mutex_unlock(&accessReaderCnt); 54 55 read(); 56 57 pthread_mutex_lock(&accessReaderCnt); 58 readerCnt--; 59 if(readerCnt == 0){ 60 pthread_mutex_unlock(&writeLock); 61 } 62 pthread_mutex_unlock(&accessReaderCnt); 63 sleep(R_SLEEP); 64 } 65 pthread_exit((void *) 0); 66 } 67 68 int main() 69 { 70 int i = 0; 71 for(i = 0; i < N_READER; i++) 72 { 73 pthread_create(&wid[i],NULL,reader,NULL); 74 } 75 for(i = 0; i < N_WRITER; i++) 76 { 77 pthread_create(&rid[i],NULL,writer,NULL); 78 } 79 while(1){ 80 sleep(10); 81 } 82 return 0; 83 }
2. 写者优先代码
1 #include "stdio.h" 2 #include <stdlib.h> 3 #include <pthread.h> 4 5 6 #define N_WRITER 2 //写者数目 7 #define N_READER 20 //读者数目 8 #define W_SLEEP 1 //控制写频率 9 #define R_SLEEP 0.5 //控制读频率 10 11 12 pthread_t wid[N_WRITER],rid[N_READER]; 13 const int MAX_RAND = 1000;//产生的最大随机数 14 int data = 0; 15 int readerCnt = 0, writerCnt = 0; 16 pthread_mutex_t accessReaderCnt = PTHREAD_MUTEX_INITIALIZER; 17 pthread_mutex_t accessWriterCnt = PTHREAD_MUTEX_INITIALIZER; 18 pthread_mutex_t writeLock = PTHREAD_MUTEX_INITIALIZER; 19 pthread_mutex_t readerLock = PTHREAD_MUTEX_INITIALIZER; 20 pthread_mutex_t outerLock = PTHREAD_MUTEX_INITIALIZER; 21 22 void write() 23 { 24 int rd = rand()%MAX_RAND; 25 printf("write %d\n",rd); 26 data = rd; 27 } 28 void read() 29 { 30 printf("read %d\n",data); 31 } 32 void * writer(void * in) 33 { 34 while(1) 35 { 36 pthread_mutex_lock(&accessWriterCnt); 37 {//临界区,希望修改 writerCnt,独占 writerCnt 38 writerCnt++; 39 if(writerCnt == 1){ 40 //阻止后续的读者加入待读队列 41 pthread_mutex_lock(&readerLock); 42 } 43 } 44 pthread_mutex_unlock(&accessWriterCnt); 45 46 47 pthread_mutex_lock(&writeLock); 48 {//临界区,限制只有一个写者修改数据 49 write(); 50 } 51 pthread_mutex_unlock(&writeLock); 52 53 pthread_mutex_lock(&accessWriterCnt); 54 {//临界区,希望修改 writerCnt,独占 writerCnt 55 writerCnt--; 56 if(writerCnt == 0){ 57 //阻止后续的读者加入待读队列 58 pthread_mutex_unlock(&readerLock); 59 } 60 } 61 pthread_mutex_unlock(&accessWriterCnt); 62 sleep(W_SLEEP); 63 } 64 pthread_exit((void *) 0); 65 } 66 67 void * reader (void * in) 68 { 69 while(1) 70 { 71 //假如写者锁定了readerLock,那么成千上万的读者被锁在这里 72 pthread_mutex_lock(&outerLock); 73 {//临界区 74 pthread_mutex_lock(&readerLock);//只被一个读者占有 75 {//临界区 76 pthread_mutex_lock(&accessReaderCnt);//代码段 1 77 {//临界区 78 readerCnt++; 79 if(readerCnt == 1){ 80 pthread_mutex_lock(&writeLock); 81 } 82 } 83 pthread_mutex_unlock(&accessReaderCnt); 84 } 85 pthread_mutex_unlock(&readerLock);//释放时,写者将优先获得readerLock 86 } 87 pthread_mutex_unlock(&outerLock); 88 89 read(); 90 91 pthread_mutex_lock(&accessReaderCnt);//代码段2 92 {//临界区 93 readerCnt--; 94 if(readerCnt == 0){ 95 pthread_mutex_unlock(&writeLock);//在最后一个并发读者读完这里开始禁止写者执行写操作 96 } 97 } 98 pthread_mutex_unlock(&accessReaderCnt); 99 100 sleep(R_SLEEP); 101 } 102 pthread_exit((void *) 0); 103 } 104 105 int main() 106 { 107 int i = 0; 108 for(i = 0; i < N_READER; i++) 109 { 110 pthread_create(&rid[i],NULL,reader,NULL); 111 } 112 for(i = 0; i < N_WRITER; i++) 113 { 114 pthread_create(&wid[i],NULL,writer,NULL); 115 } 116 while(1){ 117 sleep(10); 118 } 119 return 0; 120 }
OS: 读者写者问题(写者优先+LINUX+多线程+互斥量+代码)(转)相关推荐
- Linux多线程实践(6) --Posix读写锁解决读者写者问题
Posix读写锁 int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock,const pthread_rwlockattr_t *restr ...
- 【操作系统】读者写者问题——写者优先、寿司店问题 题目+答案
噩梦来啦~ 一]讲解 同步:多进程按一定顺序执行 互斥:多进程操作同一个临界资源 信号量>0,说明它空闲.所测试的线程可以锁定而使用它. 信号量=0,说明它被占用,测试的线程要进入睡眠队列中,等 ...
- 操作系统-读者写者问题,写者优先,多个读者可以同时读书(C++实现)
最近在学习操作系统,对读者写者问题进行了实现,还存在部分问题. 关系:读者与写者互斥.写者与写者互斥 写者优先 临界资源:书(一本) 分析:互斥可用互斥信号量mutex,写者优先可设 ...
- 代码该如何写才能自己写的容易别人看的也不痛苦
切身感受 在这个世界上,最难看懂的文档,永远是同事写的需求文档.最难看懂的代码,永远是同事写的业务代码. 我很纳闷,像Spring这样的官方英文文档,我看起来也不太费劲,但是需求文档,我却要花费极大力 ...
- Linux 多线程 ”一写多读” 模式下的无锁设计
缘起 双buffer "无锁" 设计 指针的切换 ptr 竞争条件的解决 指针访问丢失 延伸 结语 缘起 在linux多线程环境下对同一变量进行读写时,经常会遇到读写的原子性问题, ...
- 对系统学习与写博客的看法——学完《第一行代码》有感
笔者如今学习android有近8个月,此时才真正地学完一本书也甚是惭愧. 在此分享一下自己大概的学习经历.(笔者在大一下就开始学android了,学的时候只有c与c++的基础,一个学期+寒假在OJ上刷 ...
- java输出流怎样换行_Java中输出流续写和换行写方法,需要用到的构造方法的知识点...
/* FileOutputStream(String name, boolean append) 创建文件输出流以指定的名称写入文件. FileOutputStream(File file, bo ...
- php和python写爬虫-为什么写爬虫都喜欢用python?
可能很多人以为PHP只能做做网页,不知道PHP也有Simple-HTML-DOM , phpQuery, Ganon这些现成的DOM操作库吧,可能以为PHP只能自己从头用fopen/file_get_ ...
- 【手写系列】写一个迷你版的Tomcat
前言 Tomcat,这只3脚猫,大学的时候就认识了,直到现在工作中,也常会和它打交道.这是一只神奇的猫,今天让我来抽象你,实现你! Tomcat Write MyTomcat Tomcat是非常流行的 ...
最新文章
- C#列出局域网中可用SQL Server服务器(续)
- 完美解决 keil5.25 某宝Jlink无法使用问题
- js的数据类型,以及如何判断它们是哪种类型
- P1975-[国家集训队]排队【树状数组套线段树】
- PHP添加php-java-brideg模块(ubuntu环境)
- Stateflow中的真值表注意事项
- gitHub上传项目
- 获取当前上下文Activity
- Android 图片文件操作、屏幕相关、.9图片的理解
- Unity 3D开发-C#脚本语言的一些基础用法
- php memcached 加锁,用memcached实现的php锁机制
- 使用DIME协议上传文件
- 推荐国产 notebook 软件
- php 发邮件 上传附件,PHPMailer实现PHP的邮件发送,附带附件
- Ubuntu18.04下的音频录制和编辑软件Ardour及QjackCtl(jackd gui)
- 让人拍案叫绝的创意都是如何诞生的
- 计算机网络-实验一:windows网络测试工具
- Acwing2058. 笨拙的手指
- bat 打开 任务管理器
- UML与软件开发的关系