操作系统刚上两周网课老师便布置了两道现阶段本人觉得是课设的”课设“,第一道题在之前的博客中报告过了,下面是第2道题的菜鸟报告。上一篇博客中有初次做这道题的具体分析过程了,这里就侧重后面的代码实现部分的分析。

先上题目:

题目

某寺庙,有小和尚、老和尚若干。有一水缸,由小和尚提水入缸,老和尚从缸中取水饮用。水缸可容纳10桶水,水取自同一水井中,水井径窄,每次只能容一个水桶取水。水桶总数为3个,每次入、取缸水仅为1桶,且不可同时进行。试给出取水、入水的算法描述。

1、初解

1、乍一看以为只是算法描述,然后课本和一些网上资源也只是P、V操作的伪代码。因此我的分析是这样子的:
看题过程把重点信息找出来(即找出资源),然后开始进一步捋清流程。
小和尚打水入缸流程:
拿桶--------->去水井取水(互斥,P、V操作)--------->把水倒入水缸(互斥,P、V操作)--------->放桶
老和尚取水饮用流程:
拿桶--------->去水缸取水(互斥,P、V操作)--------->放桶
2、伪代码如下:

semaphore  mutex_well = 1, mutex_vat = 1;//互斥量
semaphore  pail = 3, empty = 10, full = 0;//定义自然量,empty表示水的总容量,full表示满的标志
project  small()//小和尚
{while (true){P(empty); //判断水缸是否还有容量,有则减一,程序向下执行P(pail);  //申请一个桶P(mutex_well);  //占用水井从水井中打水;  //活动V(mutex_well); //用完水井,释放资源P(mutex_vat);  //占用水缸将水倒入水缸中; //活动V(mutex_vat)  //释放资源V(pail);   //放桶V(full);  //full+1,即水缸中的水的桶数加1,注意成对出现问题!!!}
}
project old()//老和尚
{while (true){P(full); //看是否有水,有则减一,程序向下执行P(pail); //拿桶P(mutex_vat); //占用水缸从水缸中取水; //活动V(mutex_vat); //用完水缸,释放资源喝水; //此处虽可省,但有这一步更为具体形象V(pail); //放桶V(empty); //容量加一,可装入的水的桶数加1}
}

通过看课本和在网上看一些别人的总结分享,我写出了这段伪代码。写完后心里还想着,咦,老师不是说比上次的题目困难很多吗?怎么这么容易就被我搞定了(是真的比较容易,只要求这样子的答案的话)。但,无论如何当时都觉得很开心,终于把这个每周都会有的令人担忧的操作系统实操作业搞定了!(毕竟我比较菜鸟)

2、再次上课

很多人都只是写了简单的一段伪代码,毕竟题目只是说算法描述,老师也没特别说明,就只是说回去把它搞好,So…
然后就被说了:”伪代码有啥可看的”,然后便"耐心“地讲解了关于这方面的内容,主要就是信号量的具体使用吧,下课前留了一句:回去接着把题目搞好啊…
课上老师讲解了一个简单的示例,如下:

#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
HANDLE s;
int i = 100;
DWORD WINAPI fncc(LPVOID p){DWORD id = GetCurrentThreadId();srand(id);while (true) {int interval = int((double)rand() / RAND_MAX * 950 + 1050);//产生随机数并赋值printf("我是%d, 我在等待信号量\r\n", id);WaitForSingleObject(s, INFINITE);//等待信号量printf("我是%d, 我得到了信号量\r\n", id);printf("我是%d, i原来值是%d, 我把变量i赋值为 %d\r\n", id, i, interval);i = interval;Sleep(interval);printf("我是%d,之前我把i的值赋为 %d, 变量i现在的值是: %d\r\n", id, interval, i);ReleaseSemaphore(s, 1, NULL);//释放信号量printf("我是%d, 我释放了信号量\r\n", id);}
}
void main()
{s = CreateSemaphore(NULL, 1, 1, NULL);DWORD threadId;CreateThread(NULL, 4 * 1024, fncc, 0, 0, &threadId);CreateThread(NULL, 4 * 1024, fncc, 0, 0, &threadId);CreateThread(NULL, 4 * 1024, fncc, 0, 0, &threadId);CreateThread(NULL, 4 * 1024, fncc, 0, 0, &threadId);CreateThread(NULL, 4 * 1024, fncc, 0, 0, &threadId);CreateThread(NULL, 4 * 1024, fncc, 0, 0, &threadId);CreateThread(NULL, 4 * 1024, fncc, 0, 0, &threadId);CreateThread(NULL, 4 * 1024, fncc, 0, 0, &threadId);CreateThread(NULL, 4 * 1024, fncc, 0, 0, &threadId);CreateThread(NULL, 4 * 1024, fncc, 0, 0, &threadId);CreateThread(NULL, 4 * 1024, fncc, 0, 0, &threadId);CreateThread(NULL, 4 * 1024, fncc, 0, 0, &threadId);while (!_kbhit()){Sleep(1000);}
}

这个示例我倒是明白,想说的是因为使用了 下面的语句

WaitForSingleObject(s, INFINITE);
代码语句;ReleaseSemaphore(s, 1, NULL);

所以,在一个线程休眠时,他给i赋的值在输出之前不会被另外一个线程更改。
运行结果如下图:

3、再解

这次想上网参考下别人的分享都不太行了,因为很少人分享这类问题的具体代码实现,分享个人分析这类问题和P、V操作的倒是很多,从极少的资源中学到的一点点东西写到VS又不行,这里行了那里又出问题,而且网上的比较零散,几乎没有完整的,也有一些这类问题完整的分享我却看不懂,虽然大概知道是怎么个过程,但写成代码一直在提示错误…
最后只好向同学请教了,代码整理分享如下:

#include"windows.h"
#include<iostream>
using namespace std;
/*用来打开或创建一个信号量,
HANDLE  CreateSemaphore(LPSECURTTY_ATTRIBUTES IpSemaphoreAttributes,//ID   属性,一般可设为NULLLONE IINitialCount,   //initial  count  信号量初始值,必须大于等于0,而且小于最大值LONG  IMaximumCount,  //maximum  count  设置信号量的最大值,必须大于0LPCTSTR IpName        //object  name    信号量的名字,可设置为NULL。IpName不为空时,如果当前信号量名与已存在的信号量的名字相同时,则该函数表示打开该信号量。
);
*/
//说明:下面的L"sem1"前的L有的电脑不需要加的,我的电脑要加了才可以,否则显示错误。
HANDLE  well= CreateSemaphore(NULL, 1, 1, L"sem1");//井的信号量        互斥量   初值=1,最大值=1
HANDLE pail = CreateSemaphore(NULL, 3, 3, L"sem2");//小和尚老和尚用的水桶的信号量   初值=3,最大值=3
HANDLE vat = CreateSemaphore(NULL, 1, 1, L"sem4");//水缸的信号量          互斥量   初值=1,最大值=1
HANDLE full = CreateSemaphore(NULL, 0, 10, L"sem5");//缸中可以取的水的桶数  即可取多少桶水。
HANDLE empty1 = CreateSemaphore(NULL, 10, 10, L"sem6");//缸中可以容纳的水桶数  即可容纳10桶水
int count1 = 0;  //此时水缸中存储水的桶数
CRITICAL_SECTION cs;
/*每个进程中访问临界资源的那段代码称为临界区,简称cs区(临界资源是一次仅允许一个进程使用的共享资源)。
每次只准许一个进程进入临界区,进入后不允许其他进程进入。
不论是硬件临界资源,还是软件临界资源,多个进程必须互斥地对它进行访问。*//*WINAPI 视窗操作系统应用程序接口(Windows API),有非正式的简称法为WinAPI,
是微软对于Windows操作系统中可用的内核应用程序编程接口的称法。
它设计为由C/C++程序调用,而且它也是应用软件与Windows系统最直接的交互方式。
DWORD全称Double Word,是指注册表的键值,每个word为2个字节的长度,DWORD 双字即为4个字节,每个字节是8位,共32位。LPVOID是一个没有类型的指针,也就是说你可以将LPVOID类型的变量赋值给任意类型的指针,比如在参数传递时就可以把任意类型传递给一个LPVOID类型为参数的方法,然后在方法内再将这个“任意类型”从传递时的“LPVOID类型*/DWORD WINAPI youngFunc(LPVOID pArg) //小和尚打水入缸
{                                   int i = (int)pArg;          while (1)                  {     WaitForSingleObject(empty1, INFINITE);//判断水缸是否已满printf("%d号小和尚等待拿水桶\n", i);WaitForSingleObject(pail, INFINITE);//小和尚先取得水桶printf("%d号小和尚拿到了桶!\n", i);WaitForSingleObject(well, INFINITE);//小和尚先取得水井使用权,即占用水井printf("%d号小和尚打上了水\n", i);ReleaseSemaphore(well, 1, NULL);//小和尚用完水井,释放资源         /*1表示:这个信号量对象在当前基础上所要增加的值,这个值必须大于0,
如果信号量加上这个值会导致信号量的当前值大于信号量创建时指定的最大值,那么这个信号量的当前值不变,同时这个函数返回FALSE;
null,指向返回信号量上次值的变量的指针,如果不需要信号量上次的值,那么这个参数可以设置为NULL;返回值:
如果成功返回TRUE,如果失败返回FALSE,可以调用GetLastError函数得到详细出错信息;*/Sleep(1000);//休眠1sWaitForSingleObject(vat, INFINITE);//取得水缸的使用权,即占用水缸资源printf("\t\t%d号小和尚向缸中倒水\n", i);EnterCriticalSection(&cs);        //EnterCriticalSection(&cs);加锁 接下来的代码处理过程中不允许其他线程进行操作,除非遇到LeaveCriticalSection解锁                                   count1++;printf("\t\t缸中有%d桶\n", count1);LeaveCriticalSection(&cs);         //LeaveCriticalSection(&cs);解锁printf("\t\t缸中有%d桶\n", count1);LeaveCriticalSection(&cs);         //LeaveCriticalSection(&cs);解锁  到下一个EnterCriticalSection之间代码资源已经释放了,其他线程可以进行操作ReleaseSemaphore(vat, 1, NULL);//释放水缸资源EnterCriticalSection(&cs);      //加锁ReleaseSemaphore(pail, 1, NULL);//释放水桶printf("%d号小和尚放下了桶\n", i);LeaveCriticalSection(&cs);     //解锁   临界区ReleaseSemaphore(full, 1, NULL);//水缸中可使用的水的桶数加1}return NULL;
}DWORD WINAPI oldFunc(LPVOID pArg)//老和尚取水饮用
{int i = (int)pArg;while (1){Sleep(4000);WaitForSingleObject(full, INFINITE);//判断水缸中是否有水printf("\t\t\t\t\t%d号老和尚等待拿水桶\n", i);WaitForSingleObject(pail, INFINITE);//有则取得水桶printf("\t\t\t\t\t%d号老和尚拿到了桶\n", i);WaitForSingleObject(vat, INFINITE);//取得水缸的使用,占用printf("\t\t\t\t\t%d号老和尚喝到了水\n", i);EnterCriticalSection(&cs);      //加锁count1--;printf("\t\t缸中有%d桶\n", count1);LeaveCriticalSection(&cs);      //解锁ReleaseSemaphore(vat, 1, NULL);//释放水缸资源EnterCriticalSection(&cs);      //加锁ReleaseSemaphore(pail, 1, NULL);//释放水桶printf("\t\t\t\t\t\t\t%d号老和尚放下了桶\n", i);LeaveCriticalSection(&cs);      //解锁   临界区ReleaseSemaphore(empty1, 1, NULL);//水缸中可放水数量加1,即容量少了,可装入的水的桶数加1}return NULL;
}int main()
{HANDLE young;HANDLE old;InitializeCriticalSection(&cs);  //临界区初始化int i = 0;while (true){young = CreateThread(NULL, 0, youngFunc, LPVOID(i), NULL, NULL);  //创建子线程old = CreateThread(NULL, 0, oldFunc, LPVOID(i), NULL, NULL);i++;}HANDLE p[2];p[0] = young;p[1] = old;WaitForMultipleObjects(5, p, TRUE, INFINITE);WaitForMultipleObjects(5, p, TRUE, INFINITE);  /* WaitForMultipleObjects是Windows中的一个功能非常强大的函数,几乎可以等待Windows中的所有的内核对象(关于该函数的描述和例子见MSDN)。当WaitForMultipleObjects等到多个内核对象的时候,如果它的bWaitAll 参数设置为false。其返回值减去WAIT_OBJECT_0 就是参数lpHandles数组的序号。如果同时有多个内核对象被触发,这个函数返回的只是其中序号最小的那个。如果为TRUE 则等待所有信号量有效再往下执行。(FALSE 当有其中一个信号量有效时就向下执行)*/getchar();
/*getchar()函数的作用是从计算机终端(一般为键盘)获取一个无符号字符。
getchar()函数只能接收一个字符,其函数值就是从输入设备获取到的字符。*/return 0;
}

运行结果如图:


暂时就先分享到这里了,如有发现任何问题欢迎指正,或有什么改进之处还望不吝赐教,谢谢。
觉得可以的话给个小赞赞鼓励下呗!
有帮助?那就–>开启传送门 希望分享的东西对大家有所帮助哈!

操作系统:小和尚打水+老和尚喝水经典同步问题实现 菜鸟的解题全过程(附具体代码)相关推荐

  1. 操作系统-用信号量解决小和尚打水老和尚喝水问题

    题目:某寺庙,有小和尚.老和尚若干.庙内有一水缸,由小和尚提水入缸,供老和尚饮用.水缸可容纳 30 桶水,每次入水.取水仅为1桶,不可同时进行.水取自同一井中,水井径窄,每次只能容纳一个水桶取水.设水 ...

  2. 操作系统PV大题_小和尚老和尚喝水问题

    题目描述 某寺庙有小和尚和老和尚若干人,水缸一只,由小和尚提水放入缸中给老和尚引用.水缸可容纳12桶水,水取自同一口水井,水井井口直径窄,每次仅能容纳一只水桶取水,水桶总数为4个.每次小和尚只能往水缸 ...

  3. 操作系统PV操作伪代码(小和尚提水、老和尚饮水问题)

     题目: 某寺庙,有小和尚.老和尚若干.庙内有一水缸,由小和尚提水入缸,供老和尚饮用.水缸可容纳10桶水,每次入水.取水仅为1桶,不可同时进行.水取自同一井中,水井径窄,每次只能容纳一个水桶取水.设水 ...

  4. 多核多线程中小和尚老和尚取水喝水问题

    最近学习多核多线程技术,最后的考试题目是和尚的问题.题目具体如下: 某寺庙有小和尚.老和尚若干.庙内有一水缸.由小和尚提水入缸,供老和尚引用.水缸可容纳10桶水,每次入水.取水都为1桶,且不可同时进行 ...

  5. 早上喝水较健康 喝法有学问!

    2019独角兽企业重金招聘Python工程师标准>>> 导读:水是生命之源,有足够水份才能使身体保持健康,平时需适当补充水份,不是渴了才喝.据研究人体经过一夜的睡眠.唿吸,与长时未进 ...

  6. 开发者方案 · 久坐提醒 / 喝水提醒小助手·树莓派4B/咕咕机G2/涂鸦 Link SDK

    本文方案来自CSDN博主「机灵鹤」 方案详情可查看原文:https://smartcrane.blog.csdn.net/article/details/121837850 1. 项目介绍 本项目实现 ...

  7. 社畜养生必备喝水app

    社畜养生必备喝水app 有没有小宝贝们老是忘记喝水,也懒得喝水不喜欢喝水,一坐下就是一整天.其实这种习惯非常不好,坐久了不仅对肩椎不好,而且饮水量过少皮肤也会比较差.下面给大家推荐几款喝水提醒的app ...

  8. 【操作系统】某寺庙,住着一个老和尚和若干小和尚,有一个水缸,由小和尚提水入缸供老和尚饮用。水缸可以容纳10桶水,水取自同一口井中,由于水井口窄,每次只能容纳一个水桶取水,水桶总数为3个。每次往水缸中倒

    题目 某寺庙,住着一个老和尚和若干小和尚,有一个水缸,由小和尚提水入缸供老和尚饮用.水缸可以容纳10桶水,水取自同一口井中,由于水井口窄,每次只能容纳一个水桶取水,水桶总数为3个.每次往水缸中倒水与从 ...

  9. 和尚挑水 java_java编程:山上有一口缸可以装50升水,现在有15升。老和尚叫小和尚下山挑水,每次挑5升,要挑几次...

    java编程:山上有一口缸可以装50升水,现在有15升.老和尚叫小和尚下山挑水,每次挑5升,要挑几次 关注:123  答案:6  mip版 解决时间 2021-01-17 07:16 提问者妳有我霸氣 ...

最新文章

  1. VS调试时提示此项目已经过期
  2. RandomForest:随机森林预测生物标记biomarker——分类
  3. MUI框架 APP手机退出方式
  4. HTML介绍及简单模式
  5. 免费体验,阿里云智能LOGO帮你解决设计难题
  6. spark学习-31-spark2.2.0中Utils.getCallSite()的作用
  7. php centos mysql_Linux+Apache+PHP+MySQL服务器环境(CentOS篇)
  8. 58欧氏空间05——对称变换和对称矩阵、实对称矩阵的标准形、正交相似、实对称矩阵的正交相似对角化
  9. js中的instanceof运算符
  10. 获取ip地址 域名获取与解析
  11. 基于.Net C# 通信开发-网络调试助手
  12. 口袋之旅html5超强账号,《口袋之旅H5》攻略:各大排行榜冲榜攻略
  13. ZUCC_数据库系统概论实验_实验五 JDBC进阶 2
  14. 对称加密 非对称加密
  15. OpenCV图像的基本操作
  16. 最有效的穴位按摩减肥法
  17. [230506] 2021年托福阅读真题第6篇|Water and Life on Mars|15:30~16:30|16:30~19:19
  18. 宝利德余海军浙商封面 :新数智时代的修行者
  19. 智能家居(12)——树莓派USB摄像头捕捉人脸并识别
  20. android view只能在主线程操作

热门文章

  1. debian9宝塔面板安装php失败,宝塔面板安装php失败:提示No package 'libjpeg' found的解决办法...
  2. java 图片合成 工具类_Java实现的图片上传工具类完整实例
  3. Xcode7.1环境下上架iOS App到AppStore 流程 (2)
  4. c语言既适合于开发,C语言试题及答案 (1)
  5. matlab dir datenum,matlab中的datenum
  6. 安装报错_RG Magic Bullet安装报错修复方法
  7. 织梦html地图插件,织梦dede网站地图xml生成插件(图文教程)
  8. elasticsearch: 权威指南_你还不会Elasticsearch的CUD?
  9. python 数据库订阅_发布/订阅数据库和客户端编程数据库有什么区别?
  10. java中exception in_java.lang.ExceptionInInitializerError