当前位置: 首页 > news >正文

Linux学习记录--利用信号量来调度共享资源(2)

一.读者-写者问题

        在上一篇文章中,讲了利用信号量来调度共享资源的一种典型案例生产者-消费者问题,本篇讲另一个经典案例,读者-写者问题。

        读者-写者问题是互斥问题的一个概括。一组并发的线程要访问一个共享对象,例如一个主存中的数据结构,或者一个磁盘上的数据库。有些线程只读对象,而其他的线程只修改对象。修改对象的线程叫做写者。只读的线程叫做读者。写者必须拥有对对象的独占访问,而读者可以和无限多个其他读者共享对象。
读者-写者问题很常见,比如在手机上选电影票,你可以作为读者与其他读者一起浏览座位,但正在你正在预定一个座位时,此座位便被你占有,其余人士无法选择。
读者-写者问题也存在几个变种,分别基于读者和写者的优先级。下面就这两个优先级分类分别讲讲。

二.读者优先

        读者优先,要求不要让读者等待,除非已经把使用对象的权限赋予了一个写者。也就是说,读者不会因为写者在等待排在写者后面。

        下面提供一个案例:这里引入了随机数来随即生成读或写,写比较费事,设计写2/5,读3/5,他们的消耗时间也是随机生成,不过为了感受写费时,在写额外增加时间,下面同理。

#include "c_pthread_box.h"
#include <time.h>
#include <string.h>
#include <stdint.h>int msleep(long msec)               /* 自己包一层,返回 0 成功,-1 失败 */
{struct timespec ts;ts.tv_sec  = msec / 1000;ts.tv_nsec = (msec % 1000) * 1000000L;return nanosleep(&ts, NULL);    /* 精确到纳秒,可被打断 */
}/* Global variables */
int readcnt = 0;          /* The number of reader ,initally = 0 */
sem_t mutex,w;         /* This mutex protects reading (readcnt), w protect write, both initiall = 1 */int a = 10, b = 99;void* reader(void* vargp)
{int myid = (int)(intptr_t)vargp;static int rcnt = 0 ;int rid ;P(&mutex);readcnt++;rcnt++;rid = rcnt;printf("[%d],reader%d: 在读\n",myid,rid);if(readcnt == 1){ /* 如果是第一位读者,就先对写者添加限制,保障读者 */P(&w);}V(&mutex);        /* 因为读者优先,因此没到最后一位读者读完,不会解开写者的锁 *//* Critical section *//* Reading happens */int in_range = a + rand() % (b - a + 1);msleep(in_range); P(&mutex);readcnt--;if(readcnt == 0){              /* 如果是最后一位读者,退出前解除对写者的限制,让写者可去操作 */V(&w);}printf("[%d],reader%d: 读完\n",myid,rid);V(&mutex);
}void* writer(void* vargp)
{int myid = (int)(intptr_t)vargp;static int wcnt = 0;wcnt++;int wid = wcnt ;printf("[%d],writer%d: 想要写\n",myid,wid);P(&w);printf("[%d],writer%d: 在写\n",myid,wid);/* Critical section *//* Writing happens */int in_range = a + 100 + rand() % (b - a + 1);msleep(in_range); printf("[%d],writer%d: 写完\n",myid,wid);V(&w);
}int main(int argc,char** argv)
{int N , i ,w_r;
//    pthread_t rtid,wtid;if(argc != 2){fprintf(stderr, "usage: %s <N>\n", argv[0]);exit(-1);}srand((unsigned)time(NULL));          /* 播种 */N = atoi(argv[1]);pthread_t *tids = malloc(N * sizeof(pthread_t));sem_init(&mutex,0,1);sem_init(&w,0,1);for(i = 0; i < N;i++ ){w_r = rand() % (5);if(w_r <= 1){pthread_create(&tids[i],NULL,writer,(void*)(intptr_t)i);}else{pthread_create(&tids[i],NULL,reader,(void*)(intptr_t)i);}       }for(i = 0 ;i < N; i++){pthread_join(tids[i],NULL);}return 0;
}

其中一次运行结果:

当有读者正在读时,后续的读者可以直接加入,写者必须等待所有读者完成。

优点:读操作并发度高
缺点:写者可能饥饿(长时间等待)

Tips:

饥饿(starvation):饥饿就是一个线程无限期地阻塞,无法进展。例如本例,写者一直想要写,但最后也是一直等待,直到读者全部读完才能去写。

三.写者优先

        与读者优先相反,读者更有优先级,不会因写者等待而等待。

#include "c_pthread_box.h"
#include <time.h>
#include <string.h>
#include <stdint.h>int msleep(long msec)               /* 自己包一层,返回 0 成功,-1 失败 */
{struct timespec ts;ts.tv_sec  = msec / 1000;ts.tv_nsec = (msec % 1000) * 1000000L;return nanosleep(&ts, NULL);    /* 精确到纳秒,可被打断 */
}/* Global variables */
int writercnt = 0;          /* The number of writerer ,initally = 0 */
sem_t mutex,r;         /* This mutex protects writeing (writecnt), r protect read, both initiall = 1 *//* 3. 指定范围 [a,b] */int a = 10, b = 99;void* writer(void* vargp)
{int myid = (int)(intptr_t)vargp;static int wcnt = 0 ;int wid ;P(&mutex);writercnt++;wcnt++;wid = wcnt;printf("[%d],writerer%d: 在写\n",myid,wid);if(writercnt == 1){ /* 如果第一位写者,就先对读者添加限制,保障写者 */P(&r);}V(&mutex);        /* 因为写者优先,因此没到最后一位写者写完,不会解开读者的锁 *//* Critical section *//* Reading happens */int in_range = a + rand() % (b - a + 1);msleep(in_range); P(&mutex);writercnt--;if(writercnt == 0){              /* 如果是最后一位写者,退出前解除对读者的限制,让读者可去操作 */V(&r);}printf("[%d],writerer%d: 写完\n",myid,wid);V(&mutex);
}void* reader(void* vargp)
{int myid = (int)(intptr_t)vargp;static int rcnt = 0;rcnt++;int rid = rcnt ;printf("[%d],reader%d: 想要读\n",myid,rid);P(&r);printf("[%d],reader%d: 在读\n",myid,rid);/* Critical section *//* Writing happens */int in_range = a + 100 + rand() % (b - a + 1);msleep(in_range); printf("[%d],reader%d: 读完\n",myid,rid);V(&r);
}int main(int argc,char** argv)
{int N , i ,w_r;
//    pthread_t rtid,wtid;if(argc != 2){fprintf(stderr, "usage: %s <N>\n", argv[0]);exit(-1);}srand((unsigned)time(NULL));          /*  播种 */N = atoi(argv[1]);pthread_t *tids = malloc(N * sizeof(pthread_t));sem_init(&mutex,0,1);sem_init(&r,0,1);for(i = 0; i < N;i++ ){w_r = rand() % (5);if(w_r > 1){pthread_create(&tids[i],NULL,writer,(void*)(intptr_t)i);}else{pthread_create(&tids[i],NULL,reader,(void*)(intptr_t)i);}       }for(i = 0 ;i < N; i++){pthread_join(tids[i],NULL);}return 0;
}

其中一个运行结果:

当有写者等待时,后续的读者必须等待写者完成。

优点:保证写者不会饥饿
缺点:读操作并发度降低

四.总结

策略优点缺点适用场景
读者优先读并发度高写者可能饥饿读多写少
写者优先写者不会饥饿读并发度低写操作重要

进一步优化:可以实现公平的读者-写者算法,避免任何一方饥饿,如使用额外的队列管理等待顺序。

实际应用:数据库系统的并发控制、文件系统的读写锁等都基于读者-写者问题的解决方案。

http://www.dtcms.com/a/407850.html

相关文章:

  • 管理一个网站的后台怎么做做app要多少钱
  • 自动化测试篇--用例篇
  • 贵阳网络公司网站建设衣服网站模板
  • 基于MATLAB的Copula函数实现示例
  • 攻防世界-Web-simple_js
  • 【Triton 教程】triton_language.ravel
  • 微信网站建设需要那些资料嵌入式软件开发项目
  • 中建一局华江建设有限公司网站类似于wordpress的
  • 学生个人网页设计作品模板肇庆网站快速排名优化
  • 网站优化的核心不包括wordpress商城小程序
  • 整体设计 完整的逻辑链条之11 三转法论驱动的 ISO - 认知融合逻辑系统:从架构映射到自动化缝合的完整设计
  • 网站服务费怎么做分录查网站
  • 项目发布部署
  • 告别字符串拼接繁琐!Java String.format () 实用指南
  • 写小说赚钱的网站温岭市住房和城乡建设局网站
  • 厦门 网站优化宜宾公司做网站
  • 北京网站设计网站公司2021年工程造价信息
  • 有个做图片mv的网站56厦门网络公司网站
  • PaperReading:《Manipulating Multimodal Agents via Cross-Modal Prompt Injection》
  • Unity模拟《切尔诺贝利》中的控制棒
  • 创新型的网站建设域名查询ip地址
  • 离散时间信号和系统的频域分析
  • 门户网站技术方案wordpress 滑到底部自动加载
  • 公司网站百度搜不到寻找外贸客户的网站
  • 电子商务网站开发怎么设计做漫画视频在线观看网站
  • 手表大全网站广告网站模板免费下载
  • 网站布局分类汽车手机网站制作
  • Spring的AOP
  • PyQt简单做一个GUI
  • Transformer 全景解析:从原理到实践的进阶指南