OS: 读者写者问题(写者优先+LINUX+多线程+互斥量+代码)(转)
一. 引子
二. 互斥量与信号量
三. 读者优先
1. 读者
1) 写者写时,不可读
2) 有别的读者正在读,可读
2. 写者
1) 有读者正在读,不可写
2) 有写者正在写,不可写
3) 无读者正在读,无写者正在写,可写
四. 写者优先
1. 读者特点
1) 有写者正在写或者等待写,须等到没有写者才能读
2) 没有写者,可以读
2. 写者特点
1) 写者与写者互斥。当其它写者正在写时,其它写者不能写。
2) 写者与读者互斥。之前只有读者在读,当写者出现时,必须等到之前的读者都读完才能写。这尊重了之前读者的意愿。
3) 写者可以有条件地插读者的队。当前有写者正写,有读者在等,这时来了新写者,新写者可以在那些读者之前执行。这不尊重
3. 写者实现
while()
{
pthread_mutex_lock(&writeLock);
{//临界区,限制只有一个写者修改数据
write();
}
pthread_mutex_unlock(&writeLock);
}
考虑到写者对读者的影响是:当任何写者想写时,读者都必须被阻塞;并且,写者阻塞了读者并停止阻塞之前,后续的任何写者都会优先于读者执行。这就如同有一个写者队列,当第一个写者入队时,读者完全被阻塞,直到最后一个写者离开队列。
while()
{
pthread_mutex_lock(&accessWriterCnt);
{//临界区,希望修改 writerCnt,独占 writerCnt
writerCnt++;
if(writerCnt == ){
//读者与写者互斥;使用readerLock来描述;
pthread_mutex_lock(&readerLock);
}
}
pthread_mutex_unlock(&accessWriterCnt); pthread_mutex_lock(&writeLock);
{//临界区,限制只有一个写者修改数据
write();
}
pthread_mutex_unlock(&writeLock); pthread_mutex_lock(&accessWriterCnt);
{//临界区,希望修改 writerCnt,独占 writerCnt
writerCnt--;
if(writerCnt == ){
//阻止后续的读者加入待读队列
pthread_mutex_unlock(&readerLock);
}
}
pthread_mutex_unlock(&accessWriterCnt);
sleep(W_SLEEP);
}
4. 读者的实现
while()
{
pthread_mutex_lock(&readerLock);
{//临界区
read();
}
pthread_mutex_unlock(&readerLock);
}
在上面的实现中,很明显的问题是,当一个读者执行 read() 的同时,不可能有另外的读者进入临界区并执行 read() 函数了。因此,必须确保在执行 read() 函数之前对readerLock解锁。代码类似于:
while()
{
pthread_mutex_lock(&readerLock);
pthread_mutex_unlock(&readerLock);
read();
}
于是乎,现在我们可以注意到,假如写者率先锁定了 readerLock , 那么后续的读者被阻塞在 pthread_mutex_lock(&readerLock); 了,这点很正确。在写者最终释放 readerLock 之前,可能有成千上万的读者都被阻塞在 readerLock 上, readerLock 释放之后,这些读者会竞争这 readerLock,嗯,这没什么问题。问题在于,此后,将可能有新的写者有写需求并希望获得这把锁(参照一下写者代码吧),那么,此时,写者不得不和成千上万的读者一起竞争 readerLock,由于将占有 readerLock 的人使随机的,写者在数量上不占优势,将进入饥饿状态。因此,这种实现无法满足“写者优先”的需求。
while()
{
pthread_mutex_lock(&outerLock);//成千上万的读者被锁在这里
pthread_mutex_lock(&readerLock);//只被一个读者占有 pthread_mutex_unlock(&readerLock);//释放时,写者将优先获得readerLock
pthread_mutex_unlock(&outerLock);
read();
}
为了防止执行 read() 的同时,写者正在执行 write(),所以,有必要在读者的代码中,等待 write() 函数执行完,注意到写者的代码中,使用writeLock 互斥量来保护数据:
pthread_mutex_lock(&writeLock);
{//临界区,限制只有一个写者修改数据
write();
}
pthread_mutex_unlock(&writeLock);
因而,读者也可以通过对 writeLock 加锁以保证读的时候没有同时写,但是下面这种做法又导致读者无法并发:
while()
{
pthread_mutex_lock(&outerLock);//成千上万的读者被锁在这里
pthread_mutex_lock(&readerLock);//只被一个读者占有 pthread_mutex_unlock(&readerLock);//释放时,写者将优先获得readerLock
pthread_mutex_unlock(&outerLock); pthread_mutex_lock(&writeLock);//每个执行到这里的读者都企图对 writeLock 加锁
{//临界区
read();//同一时刻只能有一个读者在这里,无法实现读并发
}
pthread_mutex_unlock(&writeLock);
}
pthread_mutex_lock(&accessReaderCnt);
{//临界区
readerCnt++;
if(readerCnt == ){//第一个并发读线程
pthread_mutex_lock(&writeLock);//在第一个并发读者这里开始禁止写者执行写操作
}
}
pthread_mutex_unlock(&accessReaderCnt);
pthread_mutex_lock(&accessReaderCnt);
{//临界区
readerCnt--;
if(readerCnt == ){//最后一个并发读线程
pthread_mutex_unlock(&writeLock);//在最后一个并发读者read()结束后写者可以执行写操作
}
}
pthread_mutex_unlock(&accessReaderCnt);
while()
{ //代码段1 可能的位置 0
pthread_mutex_lock(&outerLock);//假如写者锁定了readerLock,那么成千上万的读者被锁在这里
//代码段1 可能的位置 1
pthread_mutex_lock(&readerLock);//只被一个读者占有
//代码段1 可能的位置 2
pthread_mutex_unlock(&readerLock);//释放时,写者将优先获得readerLock
//代码段1 可能的位置 3
pthread_mutex_unlock(&outerLock);
//代码段1 可能的位置 4 read();
pthread_mutex_lock(&accessReaderCnt);//代码段2
{//临界区
readerCnt--;
if(readerCnt == ){
pthread_mutex_unlock(&writeLock);//在最后一个并发读者读完这里开始禁止写者执行写操作
}
}
pthread_mutex_unlock(&accessReaderCnt);
}
经典的做法是把代码段1放到位置2,如果是这样,那么代码如下,我故意用了很多块作用域(用花括号{}表示)来凸显临界区:
while()
{
pthread_mutex_lock(&outerLock);//假如写者锁定了readerLock,那么成千上万的读者被锁在这里
{//临界区
pthread_mutex_lock(&readerLock);//只被一个读者占有
{//临界区
pthread_mutex_lock(&accessReaderCnt);//代码段 1
{//临界区
readerCnt++;
if(readerCnt == ){
pthread_mutex_lock(&writeLock);
}
}
pthread_mutex_unlock(&accessReaderCnt);
}
pthread_mutex_unlock(&readerLock);//释放时,写者将优先获得readerLock
}
pthread_mutex_unlock(&outerLock); read(); pthread_mutex_lock(&accessReaderCnt);//代码段2
{//临界区
readerCnt--;
if(readerCnt == ){
pthread_mutex_unlock(&writeLock);//在最后一个并发读者读完这里开始禁止写者执行写操作
}
}
pthread_mutex_unlock(&accessReaderCnt);
}
如此布局的意义在于,必须放在 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. 读者优先代码
/*
* 多线程,读者优先
*/ #include "stdio.h"
#include <stdlib.h>
#include <pthread.h> #define N_WRITER 3 //写者数目
#define N_READER 5 //读者数目
#define W_SLEEP 1 //控制写频率
#define R_SLEEP 1 //控制读频率 pthread_t wid[N_WRITER],rid[N_READER];
const int MAX_RAND = ;//产生的最大随机数
pthread_mutex_t writeLock = PTHREAD_MUTEX_INITIALIZER;//同一时间只能一个人写文件,互斥
pthread_mutex_t accessReaderCnt = PTHREAD_MUTEX_INITIALIZER;//同一时间只能有一个人访问 readerCnt
int data = ;
int readerCnt = ;
void write()
{
int rd = rand();
printf("write %d\n",rd);
data = rd;
}
void read()
{
printf("read %d\n",data);
}
void * writer(void * in)
{
while()
{
pthread_mutex_lock(&writeLock);
write();
pthread_mutex_unlock(&writeLock);
sleep(W_SLEEP);
}
pthread_exit((void *) );
} void * reader (void * in)
{
while()
{
pthread_mutex_lock(&accessReaderCnt);
readerCnt++;
if(readerCnt == ){
pthread_mutex_lock(&writeLock);
}
pthread_mutex_unlock(&accessReaderCnt); read(); pthread_mutex_lock(&accessReaderCnt);
readerCnt--;
if(readerCnt == ){
pthread_mutex_unlock(&writeLock);
}
pthread_mutex_unlock(&accessReaderCnt);
sleep(R_SLEEP);
}
pthread_exit((void *) );
} int main()
{
int i = ;
for(i = ; i < N_READER; i++)
{
pthread_create(&wid[i],NULL,reader,NULL);
}
for(i = ; i < N_WRITER; i++)
{
pthread_create(&rid[i],NULL,writer,NULL);
}
while(){
sleep();
}
return ;
}
2. 写者优先代码
#include "stdio.h"
#include <stdlib.h>
#include <pthread.h> #define N_WRITER 2 //写者数目
#define N_READER 20 //读者数目
#define W_SLEEP 1 //控制写频率
#define R_SLEEP 0.5 //控制读频率 pthread_t wid[N_WRITER],rid[N_READER];
const int MAX_RAND = ;//产生的最大随机数
int data = ;
int readerCnt = , writerCnt = ;
pthread_mutex_t accessReaderCnt = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t accessWriterCnt = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t writeLock = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t readerLock = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t outerLock = PTHREAD_MUTEX_INITIALIZER; void write()
{
int rd = rand()%MAX_RAND;
printf("write %d\n",rd);
data = rd;
}
void read()
{
printf("read %d\n",data);
}
void * writer(void * in)
{
while()
{
pthread_mutex_lock(&accessWriterCnt);
{//临界区,希望修改 writerCnt,独占 writerCnt
writerCnt++;
if(writerCnt == ){
//阻止后续的读者加入待读队列
pthread_mutex_lock(&readerLock);
}
}
pthread_mutex_unlock(&accessWriterCnt); pthread_mutex_lock(&writeLock);
{//临界区,限制只有一个写者修改数据
write();
}
pthread_mutex_unlock(&writeLock); pthread_mutex_lock(&accessWriterCnt);
{//临界区,希望修改 writerCnt,独占 writerCnt
writerCnt--;
if(writerCnt == ){
//阻止后续的读者加入待读队列
pthread_mutex_unlock(&readerLock);
}
}
pthread_mutex_unlock(&accessWriterCnt);
sleep(W_SLEEP);
}
pthread_exit((void *) );
} void * reader (void * in)
{
while()
{
//假如写者锁定了readerLock,那么成千上万的读者被锁在这里
pthread_mutex_lock(&outerLock);
{//临界区
pthread_mutex_lock(&readerLock);//只被一个读者占有
{//临界区
pthread_mutex_lock(&accessReaderCnt);//代码段 1
{//临界区
readerCnt++;
if(readerCnt == ){
pthread_mutex_lock(&writeLock);
}
}
pthread_mutex_unlock(&accessReaderCnt);
}
pthread_mutex_unlock(&readerLock);//释放时,写者将优先获得readerLock
}
pthread_mutex_unlock(&outerLock); read(); pthread_mutex_lock(&accessReaderCnt);//代码段2
{//临界区
readerCnt--;
if(readerCnt == ){
pthread_mutex_unlock(&writeLock);//在最后一个并发读者读完这里开始禁止写者执行写操作
}
}
pthread_mutex_unlock(&accessReaderCnt); sleep(R_SLEEP);
}
pthread_exit((void *) );
} int main()
{
int i = ;
for(i = ; i < N_READER; i++)
{
pthread_create(&rid[i],NULL,reader,NULL);
}
for(i = ; i < N_WRITER; i++)
{
pthread_create(&wid[i],NULL,writer,NULL);
}
while(){
sleep();
}
return ;
}
OS: 读者写者问题(写者优先+LINUX+多线程+互斥量+代码)(转)的更多相关文章
- Linux多线程实践(6) --Posix读写锁解决读者写者问题
Posix读写锁 int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock, const pthread_rwlockattr_t *rest ...
- linux多线程编程——读者优先、写者优先问题
读者优先描述 如果读者来: 1) 无读者.写着,新读者可以读: 2) 无写者等待,但有其他读者正在读,新读者可以读: 3) 有写者等待,但有其他读者正在读,新读者可以读: 4) 有写者写,新读者等 如 ...
- Atitit s2018.2 s2 doc list on home ntpc.docx \Atiitt uke制度体系 法律 法规 规章 条例 国王诏书.docx \Atiitt 手写文字识别 讯飞科大 语音云.docx \Atitit 代码托管与虚拟主机.docx \Atitit 企业文化 每日心灵 鸡汤 值班 发布.docx \Atitit 几大研发体系对比 Stage-Gat
Atitit s2018.2 s2 doc list on home ntpc.docx \Atiitt uke制度体系 法律 法规 规章 条例 国王诏书.docx \Atiitt 手写文字识别 ...
- 《自己动手写CPU》写书评获赠书活动结果
<自己动手写CPU>写书评获赠图书的读者有: 京东:8***2.16号哨兵.magicyu.kk6803.jddickyd.杰出的胡兵 亚马逊:徐贺.马先童.jaychen.farmfar ...
- CS代码代写, 程序代写, java代写, python代写, c/c++代写,csdaixie,daixie,作业代写,代写
互联网一线工程师程序代写 微信联系 当天完成特色: 互联网一线工程师 24-48小时完成.用心代写/辅导/帮助客户CS作业. 客户反馈与评价 服务质量:保证honor code,代码原创.参考课程sl ...
- vavr:让你像写Scala一样写Java
本文阅读时间大约7分钟. Hystrix是Netflix开源的限流.熔断降级组件,去年发现Hystrix已经不再更新了,而在github主页上将我引导到了另一个替代项目--resilience4j,这 ...
- C++代写,代写C++,C++程序代写,C++ assignment代写
C++代写,代写C++,C++程序代写 关于C++代写,我们的涉猎范围: C++数据结构.算法题目 C++操作系统os题目 C++网络编程networking题目 C++ Linux题目 C++ Wi ...
- 象写程序一样写博客:搭建基于github的博客
象写程序一样写博客:搭建基于github的博客 前言 github 真是无所不能.其 Pages 功能 支持上传 html,并且在页面中显示.于是有好事者做了一个基于 github 的博客管理工具 ...
- 磁盘IO单线程顺序写时最快的,如果多线程写,磁盘的磁头要不断重新寻址,所以写入速度反而会慢
(1) 读写最好还是不要多线程,硬盘读写的速度有限,单线程时已经满负荷了,多线程又会增加线程之间的切换,会增加时间. 如果想增加读写速度,应该增加硬盘,做raid (2)首先是硬盘的写入是串行的,CP ...
随机推荐
- day05_01 鸡汤+内容回顾
推荐电影: 1.被解救的姜戈 2.华尔街之狼 3.阿甘正传 4.辛德勒的名单 5.肖申克的救赎 6.上帝之城 7.焦土之城 8.绝美之城 打印多行 msg = "hello 1 hello ...
- EOJ Monthly 2018.3
985月赛我只喜欢ECNU.jpg A. 打工时不可能打工的 Time limit per test: 2.0 seconds Memory limit: 256 megabytes 我 Ayano ...
- IO Streams:扫描
简介 Scanner类被用于输入的格式化中断,并将其移到Tokens中,然后对其单个的Tokens根据其数据类型进行翻译. 从input--Tokens 默认情况下,一个Scanner使用 空格 键去 ...
- 【bzoj4260】Codechef REBXOR Trie树
题目描述 输入 输入数据的第一行包含一个整数N,表示数组中的元素个数. 第二行包含N个整数A1,A2,…,AN. 输出 输出一行包含给定表达式可能的最大值. 样例输入 5 1 2 3 1 2 样例输出 ...
- 性能学习之六---socket接口测试
socket协议较底层,所以是一个万能协议.socket发的是数据包,所以较难看懂. 下面我们来讲解socket接口测试. 大致思路为:新建sever端和client端---建立连接---发送数据 一 ...
- 【Luogu】P3809后缀排序(后缀数组模板)
题目链接 今天终于学会了后缀数组模板qwq 不过只会模板emmmm 首先我们有一本蓝书emmmmmm 然后看到蓝书221页代码之后我就看不懂了 于是请出rqy rqy: 一开始那是个对单个字符排序的操 ...
- (转)解决fasterxml中string字符串转对象json格式错误问题(无引号 单引号问题)
原文地址:解决fasterxml中string字符串转对象json格式错误问题 com.fasterxml.jackson.databind.ObjectMapper mapper = new com ...
- UVa11021 Tribles
概率 递推 每只麻球都是独立计算的. 可以递推,设f[i]表示一只麻球经过i天死光的概率,那么f[i]的k次方就是k只麻球经过i天死光的概率. 则f[i]=p[0]+p[1]*f[i-1]^1+p[2 ...
- TYVJ 1305 最大子序和 ++ 烽火传递
描述 输入一个长度为n的整数序列,从中找出一段不超过M的连续子序列,使得整个序列的和最大. 例如 1,-3,5,1,-2,3 当m=4时,S=5+1-2+3=7当m=2或m=3时,S=5+1=6 输入 ...
- 【kindeditor】KindEditor获取多个textarea文本框的值并判断非空
kindeditor官网:http://kindeditor.net/demo.php 如何获取多个KindEditor中textarea文本框的值,方式很多种(带有HTML标签). var intr ...