理发师问题 - 信号量实现版

     问题描述:
     一个理发店由一个有几张椅子的等待室和一个放有一张理发椅的理发室组成。
     1. 若没有要理发的顾客,则理发师去睡觉;
     2. 若一顾客进入理发店,理发师正在为别人理发,且等待室有空椅子,则该顾客就找张椅子按顺序坐下;
     3. 若一顾客进入理发店,理发师在睡觉,则叫醒理发师为该顾客理发;
     4. 若一顾客进入理发店且所有椅子都被占用了,则该顾客就离开。
    伪码实现:

    引入3个信号量和一个控制变量:

    1)控制变量waiting用来记录等候理发的顾客数,初值均为0; 

    2)信号量customers用来记录等候理发的顾客数,并用作阻塞理发师进程,初值为0; 

    3)信号量barbers用来记录正在等候顾客的理发师数,并用作阻塞顾客进程,初值为0(刚开始时理发师在睡觉,所以理发师这个资源数目为0); 

    4)信号量mutex用于互斥,初值为1. 

    关于p,v操作:

     P操作可以看做是申请一个资源,不管这个资源有没有都将这个资源的数目减1,如果现在资源数目小于0,就阻塞。

     v操作就是释放资源,先将这个资源的数目加1,如果发现这个资源数目小于等于0,就会唤醒在阻塞队列上的一个进程

看看PV原语实现:

 1 顾客信号量 = 0
 2 理发师信号量 = 0
 3 互斥信号量mutex = 1 // 椅子是理发师和顾客精进程都可以访问的临界区
 4 int 空椅子数量 = N     //所有的椅子数量
 5
 6 理发师(线程/进程)
 7 While(true){        //持续不断地循环
 8   P(顾客)          //试图为一位顾客服务,如果没有他就睡觉(进程阻塞)
 9   P(互斥信号量)     //如果有顾客,这时他被叫醒(理发师进程被唤醒),要修改空椅子的数量
10     空椅子数量++     //一张椅子空了出来
11   V(理发师)        //现在有一个醒着的理发师,理发师准备理发,多个顾客可以竞争理发师互斥量,但是只有一个顾客进程可以被唤醒并得到服务
12   V(互斥信号量)    //释放椅子互斥量,使得进店的顾客可以访问椅子的数量以决定是否进店等待
13   /* 理发师在理发 */
14 }
15
16
17 顾客(线程/进程)
18 while(true)
19 {   //持续不断地循环
20     P(互斥信号量)     //想坐到一张椅子上
21     if (空椅子数量 > 0)
22     { //如果还有空着的椅子的话
23         空椅子数量--        //顾客坐到一张椅子上了
24         V(顾客)           //通知理发师,有一位顾客来了
25         V(互斥信号量)     //顾客已经坐在椅子上等待了,访问椅子结束,释放互斥量
26         P(理发师)         //该这位顾客理发了,如果理发师还在忙,那么他就等着(顾客进程阻塞)
27         /* 竞争到了理发师则该顾客开始理发 */
28     }
29     else
30    {   //没有空着的椅子
31         V(互斥信号标)     //不要忘记释放被锁定的椅子
32         /* 顾客没有理发就走了 */
33     }
34 }

首先是信号量的实现版本:

 1 #include <stdio.h>
 2 #include <semaphore.h>
 3 #include <pthread.h>
 4 #define CHAIRS 3
 5
 6 //这个计数器才是共享资源
 7 int waiting=0;    //控制变量, 用来记录等候理发的顾客数
 8
 9 sem_t customers;    //用来记录等候理发的顾客数,并用作阻塞理发师进程
10 sem_t barbers;    //用来记录正在等候顾客的理发师数,并用作阻塞顾客进程
11 sem_t mutex;    //用于线程互斥
12
13 void cut_hair()
14 {
15     printf("理发ing\n");
16 }
17
18 void sleeping()
19 {
20     printf("sleeping\n");
21 }
22
23 void *barber(void *arg)
24 {
25     while(1){
26         sem_wait(&customers);    //若无顾客,理发师睡眠
27         sem_wait(&mutex);
28         waiting--;
29         printf("理发师:等待顾客-1,还剩%d人等待 \n", waiting);
30         sem_post(&barbers);
31         sem_post(&mutex);
32         cut_hair();        //理发ing, 这个时候顾客已经独享理发师了,所以不在临界区
33
34         sem_wait(&mutex);
35         if(waiting==0){        //没人就长眠去呗
36             sem_post(&mutex);
37             break;
38         }
39         sem_post(&mutex);
40     }
41     pthread_exit(NULL);
42 }
43
44 void get_cut()
45 {
46     printf("顾客%u: 理发ing\n",(unsigned int)pthread_self());
47 }
48
49 void *customer(void *arg)
50 {
51     sem_wait(&mutex);    //互斥
52     if(waiting < CHAIRS){    //等候的人比椅子少
53         waiting++;    //等候的人+1
54         sem_post(&customers);    //多了一个顾客
55         sem_post(&mutex);
56         sem_wait(&barbers);    //如果没有理发师了,那顾客就在椅子上等着
57         get_cut();
58     }
59     else{
60         printf("顾客%u: 没椅子了,走人 \n", (unsigned int)pthread_self());
61         sem_post(&mutex);
62     }
63     sem_post(&mutex);    //如果前面没有椅子了,就直接走了
64     pthread_exit(NULL);
65 }
66
67
68
69 int main(int argc, char const *argv[])
70 {
71     void * retval;
72     int res=0;
73     int i;
74     sem_init(&customers, 0, 0);    //没有顾客
75     sem_init(&barbers, 0, 0);    //没有理发师,都在睡觉呢
76     sem_init(&mutex, 0, 1);        //实现互斥
77
78     pthread_t bar, cus[6];
79     res+=pthread_create(&bar, NULL, barber, NULL);
80     for(i=0; i<6; i++){
81         res+=pthread_create(&cus[i], NULL, customer, NULL);
82     }
83     if(res!=0){
84         printf("线程创建失败!\n");
85         pthread_exit(NULL);
86     }
87     printf("线程创建成功\n");
88     pthread_join(bar,&retval);
89     for(i=0; i<6; i++){
90         pthread_join(cus[i],&retval);
91     }
92     return 0;
93 }

时间: 2024-10-05 19:14:21

理发师问题 - 信号量实现版的相关文章

linux服务器开发二(系统编程)--线程相关

线程概念 什么是线程 LWP:Light Weight Process,轻量级的进程,本质仍是进程(在Linux环境下). 进程:独立地址空间,拥有PCB. 线程:也有PCB,但没有独立的地址空间(共享). 进程与线程的区别:在于是否共享地址空间. 独居(进程). 合租(线程). Linux下: 线程:最小的执行单位. 进程:最小分配资源单位,可看成是一个线程的进程. 安装man文档 sudo apt-get install glibc-doc sudo apt-get install manp

2017/05/08学习笔记

我们将处理器的指令集架构和处理器的微体系结构区分开来:指令集架构描述的是每条机器代码效果,而微体系结构描述的是处理器实际上是如何实现的. 运行程序 当我们在键盘上输入字符串./hello后,shell程序将字符逐一读入寄存器,再把它放到内存中.利用直接存储器存取技术,数据可以不通过处理器而直接从磁盘到达内存. 一旦目标文件hello中的代码和数据被加载到主存,处理器就开始执行hello程序的main程序中的机器语言指令.这些指令将"hello,world\n"字符串中的字节从主存复制到

linux系统编程--线程同步

同步概念 所谓同步,即同时起步,协调一致.不同的对象,对“同步”的理解方式略有不同. 如,设备同步,是指在两个设备之间规定一个共同的时间参考: 数据库同步,是指让两个或多个数据库内容保持一致,或者按需要部分保持一致: 文件同步,是指让两个或多个文件夹里的文件保持一致.等等 而,编程中.通信中所说的同步与生活中大家印象中的同步概念略有差异.“同”字应是指协同.协助.互相配合.主旨在协同步调,按预定的先后次序运行. 线程同步 同步即协同步调,按预定的先后次序运行. 线程同步,指一个线程发出某一功能调

信号量解决理发师问题(barber)

问题描述及思路 代码 一些细节见注释 这里ret应该用int..忘了改了. 运行结果 因为座位数和到来最大间隔的原因,没有出现全部椅子被占用的情况 原文地址:https://www.cnblogs.com/lqerio/p/11117663.html

C#版无人驾驶汽车(附源码)

一,简单问题复杂化: 100公里/1小时的速度,在日常生活中是比较常见的速度,把它转换为其它单位: 100公里/1小时 ≈ 28米/1秒 100公里/1小时 ≈ 2800厘米/秒 如果想要无人驾驶汽车达到厘米级的位移监测.探测器扫描路况时,每秒上传2800次数据给PC机.若一辆汽车有10个探测器,就意味着每秒的并发量为2.8W次/秒. 2.8W次/秒的并发量,在网站上肯定会采用分布式,缓存,读写分离,集群技术,关键还有这个数据的存储,到底用二维数据库,还是用NOSQL.这些问题是不是让你很头痛?

《现代操作系统(原书第3版)》pdf

下载地址:网盘下载 内容简介  · · · · · · 本书是操作系统领域的经典之作,与第2版相比,增加了关于Linux.Windows Vista和Symbian操作系统的详细介绍.书中集中讨论了操作系统的基本原理,包括进程.线程.存储管理.文件系统.输入/输出.死锁等,同时还包含了有关计算机安全.多媒体操作系统.掌上计算机操作系统.微内核.多核处理机上的虚拟机以及操作系统设计等方面的内容.此外,还在第2版的基础上对部分习题进行了增删,更有助于读者学习和对知识的理解及掌握. 本书适合作为高等院

《深入理解计算机系统(原书第三版)》pdf

下载地址:网盘下载 内容简介  · · · · · · 和第2版相比,本版内容上*大的变化是,从以IA32和x86-64为基础转变为完全以x86-64为基础.主要更新如下: 基于x86-64,大量地重写代码,首次介绍对处理浮点数据的程序的机器级支持. 处理器体系结构修改为支持64位字和操作的设计. 引入更多的功能单元和更复杂的控制逻辑,使基于程序数据流表示的程序性能模型预测更加可靠. 扩充关于用GOT和PLT创建与位置无关代码的讨论,描述了更加强大的链接技术(比如库打桩). 增加了对信号处理程序

信号量与PV操作

在操作系统中进程之间经常会存在互斥和同步两种关系.为了有效处理这种情况,W.Dijskra在1965年提出信号量和PV操作的概念(1)信号量:一种特殊的变量,表现形式是一个整型S和一个队列(2)P操作:也成为"down()和wait()操作",使S=S-1,若S<0,进程暂停执行并放入信号量的等待队列.(3)V操作,也称为"up()和signal()操作",使S=S+1,若S<=0,唤醒等待队列中的一个进程. PV操作属于进程的低级通信. 利用信号量和P

C# 给某个方法设定执行超时时间 C#函数运行超时则终止执行(任意参数类型及参数个数通用版)

在某些情况下(例如通过网络访问数据),常常不希望程序卡住而占用太多时间以至于造成界面假死. 在这时.我们可以通过Thread.Thread + Invoke(UI)或者是 delegate.BeginInvoke 来避免界面假死, 但是这样做时,某些代码或者是某个方法的执行超时的时间还是无法操控的.那么我们又是否有一种比较通用的方法.来设定某一个方法的执行超时的时间,让该其一旦超过指定时间则跳出指定方法.进而继续向下执行呢? 答案当然是肯定的. delegate.BeginInvoke可以实现代