操作系统课程设计-Windows 线程的互斥和同步

news2024/11/23 14:55:36

目录

 前言

1 实验题目

2 实验目的

3 实验内容

3.1 步骤

3.2 关键代码

3.2.1 创建生产者和消费者进程

3.2.2 生产者和消费者进程

4 实验结果与分析

5 代码


 前言

         本实验为课设内容,博客内容为部分报告内容,仅为大家提供参考,请勿直接抄袭,另外,本次实验所用平台是dev c++5.11

1 实验题目

        实验四 Windows线程的互斥和同步

2 实验目的

        (1) 回顾操作系统进程、线程的有关概念,加深对 Windows 线程的理解。
        (2) 了解互斥体对象,利用互斥与同步操作编写生产者-消费者问题的并发程序,加深对 P (即semWait)、V(即 semSignal)原语以及利用 P、V 原语进行进程间同步与互斥操作的理解。

3 实验内容

3.1 步骤

        (1)步骤1:打开Dev-C++5.11 新建一个文件,命名为实验4,并保存为cpp文件。

        (2)步骤2:将清单5-1的源代码复制到实验4.cpp文件中,并点击编译按钮将其编译成可执行文件,再进入到保存文件的目录下,右键,选择在终端打开,运行编译好的可执行文件,观察运行结果。

        (3)步骤3:阅读源程序,找出创建线程的WindowsAPI函数,及其执行的第一个函数。

        (4)步骤4:调整生产者和消费者线程的数量,观察运行结果。

        (5)步骤5:按清单5-1代码中的注释修改内容,观察运行结果。

        (6)步骤 6:根据步骤 4 的结果,并查看 MSDN,回答下列问题:

                1)CreateMutex 中有几个参数,各代表什么含义。

                2)CreateSemaphore 中有几个参数,各代表什么含义,信号量的初值在第几个参数中。

                3)程序中 P、V 原语所对应的实际 Windows API 函数是什么,写出这几条语句。

               4)CreateMutex 能用 CreateSemaphore 替代吗?尝试修改程序 5-1,将信号量 Mutex 完全用 CreateSemaphore 及相关函数实现。写出要修改的语句。

3.2 关键代码

3.2.1 创建生产者和消费者进程

//创建生产者线程
	for (int i=0; i<PRODUCERS_COUNT; ++i) {
		hThreads[i]=CreateThread(NULL,0,Producer,NULL,0,&producerID[i]);
		if (hThreads[i]==NULL) return -1;
	}
//创建消费者线程
	for (int i=0; i<CONSUMERS_COUNT; ++i) {
	hThreads[PRODUCERS_COUNT+i]=CreateThread(NULL,0,Consumer,NULL,0,&consumerID[i]);
		if (hThreads[i]==NULL) return -1;
	}

3.2.2 生产者和消费者进程

//生产者
DWORD WINAPI Producer(LPVOID lpPara) {
	while(p_ccontinue) {
		WaitForSingleObject(EmptySemaphore,INFINITE); //p(empty);
		WaitForSingleObject(Mutex,INFINITE); //p(mutex);
		Produce();
		Append();
		Sleep(1500);
		ReleaseMutex(Mutex); //V(mutex);
		ReleaseSemaphore(FullSemaphore,1,NULL); //V(full);
	}
	return 0;
}
//消费者
DWORD WINAPI Consumer(LPVOID lpPara) {
	while(p_ccontinue) {
		WaitForSingleObject(FullSemaphore,INFINITE);//P(full);
		WaitForSingleObject(Mutex,INFINITE); //P(mutex);
		Take();
		Consume();
		Sleep(1500);
		ReleaseMutex(Mutex); //V(mutex);
		ReleaseSemaphore(EmptySemaphore,1,NULL); //V(empty);
	}
	return 0;
}

4 实验结果与分析

(1)执行步骤2后的部分结果如下图所示:

图1.1 生产者和消费者的部分运行结果

(2)步骤3中,线程的第一个执行函数是Producer或Consumer函数,他们是创建线程API函数的第3个参数。

(3)执行步骤4后,当消费者数量大于生产者数量时,可以在控制台看到消费者会经常因为资源不足而等待生产者,其结果如下图所示:

图1.2 调整生产者和消费者数量后的部分结果

(4)执行步骤5后,会看到控制台没有输出,因为这时候生产者可用的缓冲区数量为0,消费者的可用的初始产品数量为0,导致程序进入循环等待状态,无法继续执行下去,结果如下图所示:

图1.3 按注释修改后的控制台输出结果

(5)步骤6相关问题:

1)CreateMutex中有3个参数,各参数的含义是:

LPSECURITY_ATTRIBUTES lpMutexAttributes, // 指向安全属性的指

BOOL bInitialOwner, // 初始化互斥对象的所有者,如果希望进程立即拥有互斥体则设为FALSE

LPCTSTR lpName // 指向互斥对象名的指针

2)CreateSemaphore中有四个参数,各参数的含义是:LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,//指向 SECURITY_ATTRIBUTES 结构的指针。 如果此参数为 NULL,则子进程无法继承句柄。

LONG lInitialCount,//信号灯对象的初始计数。 此值必须大于或等于零,并且小于或等于 lMaximumCount。

LONG lMaximumCount,//信号量对象的最大计数。 此值必须大于零。

LPCSTR lpName//信号灯对象的名称。 名称限制为 MAX_PATH 个字符。 名称比较区分大小写。

3)P对应WaitForSingleObject(EmptySemaphore,INFINITE)和WaitForSingleObject(Mutex,INFINITE),V对应ReleaseMutex(Mutex)和ReleaseSemaphore(FullSemaphore,1,NULL)。

4)可以,需要修改的代码是:将Mutex = CreateMutex(NULL,FALSE,NULL)改为Mutex = CreateSemaphore(NULL,1,1,NULL),将ReleaseMutex(Mutex)改为ReleaseSemaphore(Mutex,1,NULL)。

5 代码

注意,代码是按照步骤6修改之后的

#include <windows.h>
#include <iostream>
const unsigned short SIZE_OF_BUFFER = 2; //缓冲区长度
unsigned short ProductID = 0; //产品号
unsigned short ConsumeID = 0; //将被消耗的产品号
unsigned short in = 0; //产品进缓冲区时的缓冲区下标
unsigned short out = 0; //产品出缓冲区时的缓冲区下标
int buffer[SIZE_OF_BUFFER]; //缓冲区是个循环队列
bool p_ccontinue = true; //控制程序结束
HANDLE Mutex; //用于线程间的互斥
HANDLE FullSemaphore; //当缓冲区满时迫使生产者等待
HANDLE EmptySemaphore; //当缓冲区空时迫使消费者等待
DWORD WINAPI Producer(LPVOID); //生产者线程
DWORD WINAPI Consumer(LPVOID); //消费者线程
int main() {
//创建各个互斥信号
//注意,互斥信号量和同步信号量的定义方法不同,互斥信号量调用的是 CreateMutex 函数,
//同步信号量调用的是 CreateSemaphore 函数,函数的返回值都是句柄。
	//Mutex = CreateMutex(NULL,FALSE,NULL);
	Mutex = CreateSemaphore(NULL,1,1,NULL);
	//第一个参数的SIZE_OF_BUFFER含义应该是有SIZE_OF_BUFFER个可供消费的产品
	//第二个SIZE_OF_BUFFER的含义应该是大小
	EmptySemaphore = CreateSemaphore(NULL,SIZE_OF_BUFFER,SIZE_OF_BUFFER,NULL);
	//将上句做如下修改,看看结果会怎样,会不输出
	//EmptySemaphore = CreateSemaphore(NULL,0,SIZE_OF_BUFFER-1,NULL);
	//参数的0含义应该是有0个可供消费的产品,第二个SIZE_OF_BUFFER的含义应该是大小
	FullSemaphore = CreateSemaphore(NULL,0,SIZE_OF_BUFFER,NULL);
//调整下面的数值,可以发现,当生产者个数多于消费者个数时,
//生产速度快,生产者经常等待消费者;反之,消费者经常等待
	const unsigned short PRODUCERS_COUNT = 3; //生产者的个数
	const unsigned short CONSUMERS_COUNT = 1; //消费者的个数
//总的线程数
	const unsigned short THREADS_COUNT = PRODUCERS_COUNT+CONSUMERS_COUNT;
	HANDLE hThreads[THREADS_COUNT]; //各线程的 handle
	DWORD producerID[PRODUCERS_COUNT]; //生产者线程的标识符
	DWORD consumerID[CONSUMERS_COUNT]; //消费者线程的标识符
//创建生产者线程
	for (int i=0; i<PRODUCERS_COUNT; ++i) {
		hThreads[i]=CreateThread(NULL,0,Producer,NULL,0,&producerID[i]);
		if (hThreads[i]==NULL) return -1;
	}
//创建消费者线程
	for (int i=0; i<CONSUMERS_COUNT; ++i) {

		hThreads[PRODUCERS_COUNT+i]=CreateThread(NULL,0,Consumer,NULL,0,&consumerID[i]);
		if (hThreads[i]==NULL) return -1;
	}
	while(p_ccontinue) {
		if(getchar()) { //按回车后终止程序运行
			p_ccontinue = false;
		}
	}
	return 0;
}
//生产一个产品。简单模拟了一下,仅输出新产品的 ID 号
void Produce() {
	std::cout << std::endl<< "Producing " << ++ProductID << " ... ";
	std::cout << "Succeed" << std::endl;
}
//把新生产的产品放入缓冲区
void Append() {
	std::cerr << "Appending a product ... ";
	buffer[in] = ProductID;
	in = (in+1)%SIZE_OF_BUFFER;
	std::cerr << "Succeed" << std::endl;
//输出缓冲区当前的状态
	for (int i=0; i<SIZE_OF_BUFFER; ++i) {
		std::cout << i <<": " << buffer[i];
		if (i==in) std::cout << " <-- 生产";//生产者优先
		if (i==out) std::cout << " <-- 消费";
		std::cout << std::endl;
	}
}
//从缓冲区中取出一个产品
void Take() {
	std::cerr << "Taking a product ... ";
	ConsumeID = buffer[out];
	buffer[out] = 0;
	out = (out+1)%SIZE_OF_BUFFER;
	std::cerr << "Succeed" << std::endl;
//输出缓冲区当前的状态
	for (int i=0; i<SIZE_OF_BUFFER; ++i) {
		std::cout << i <<": " << buffer[i];
		if (i==in) std::cout << " <-- 生产";//生产者优先
		if (i==out) std::cout << " <-- 消费";
		std::cout << std::endl;
	}
}
//消耗一个产品
void Consume() {
	std::cout << "Consuming " << ConsumeID << " ... ";
	std::cout << "Succeed" << std::endl;
}
//生产者
DWORD WINAPI Producer(LPVOID lpPara) {
	while(p_ccontinue) {
		WaitForSingleObject(EmptySemaphore,INFINITE); //p(empty);
		WaitForSingleObject(Mutex,INFINITE); //p(mutex);
		Produce();
		Append();
		Sleep(1500);
		//ReleaseMutex(Mutex); //V(mutex);
		ReleaseSemaphore(Mutex,1,NULL); //V(full);
		ReleaseSemaphore(FullSemaphore,1,NULL); //V(full);
	}
	return 0;
}
//消费者
DWORD WINAPI Consumer(LPVOID lpPara) {
	while(p_ccontinue) {
		WaitForSingleObject(FullSemaphore,INFINITE);//P(full);
		WaitForSingleObject(Mutex,INFINITE); //P(mutex);
		Take();
		Consume();
		Sleep(1500);
		//ReleaseMutex(Mutex); //V(mutex);
		ReleaseSemaphore(Mutex,1,NULL); //V(full);
		ReleaseSemaphore(EmptySemaphore,1,NULL); //V(empty);
	}
	return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1395023.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SqlAlchemy使用教程(五) ORM API 编程入门

SqlAlchemy使用教程(一) 原理与环境搭建SqlAlchemy使用教程(二) 入门示例及编程步骤SqlAlchemy使用教程(三) CoreAPI访问与操作数据库详解SqlAlchemy使用教程(四) MetaData 与 SQL Express Language 的使用SqlAlchemy使用教程(五) ORM API 编程入门 前一章用SQL表达式(SQL Expr…

机器学习之卷积神经网络

卷积神经网络是一类包含卷积计算且具有深度结构的前馈神经网络,是深度学习的代表算法之一。卷积神经网络具有表征学习能力,能够按其阶层结构对输入信息进行平移不变分类,因此又称为SIANN。卷积神经网络仿照生物的视知觉机制构建,可以进行监督学习和非监督学习,其隐含层内的…

【Internet Protocol】ip介绍,如何组局域网实现远程桌面和文件共享

文章目录 1.何为“上网”1.1 定义1.2 为什么连了WiFi就能上网了&#xff1f; 2.ip2.1 什么是ip2.2 为什么区分广域网和局域网&#xff0c;ip的唯一性2.3 如何查看设备的ip2.4 什么叫"ping"2.5 区分是否两个ip是否在同一局域网2.5.1 最稳妥的方式&#xff1a;ip&m…

Linux第31步_了解STM32MP157的TF-A

了解STM32MP157的TF-A&#xff0c;为后期移植服务。 一、指令集 ARMV8提供了两种指令集:AAarch64和AArch32&#xff0c;根据字面意思就是64位和32位。 ARMV7提供的指令集是AArch32。 二、TF-A 指令集是AArch64的芯片&#xff0c;TF-A有&#xff1a;bl1、bl2、bl31、bl32 和…

【Linux】进入一个目录需要什么权限-目录的权限

Linux目录权限 在Linux中&#xff0c;目录也是文件&#xff0c;是文件就有属性&#xff0c;就有权限 在Linux中&#xff0c;我们可以通过cd命令进入目录 那么我们要进入一个目录&#xff0c;需要有什么权限呢&#xff1f; 目录和普通文件一样&#xff0c;也是有权限的 测试证…

分布式Erlang/OTP(学习笔记)(一)

Erlang分布式基础 假设你在机器A和机器B上各跑着一个Simple Cache应用的实例。要是在机器A的缓存上插人一个键/值对之后&#xff0c;从机器B上也可以访问&#xff0c;那可就好了。显然&#xff0c;要达到这个目的&#xff0c;机器A必须以某种方式将相关信息告知给机器B。传递该…

Cinder组件作用

1、Cinder下发的流程 &#xff08;1&#xff09;Cinder-api接受上层发送的创建请求&#xff0c;然后把请求下发给Cinder-scheduler调度服务 &#xff08;2&#xff09;Cinder-scheduler调度服务&#xff0c;计算出哪个主机更适合创建&#xff0c;计算出来之后再把请求下发到Ci…

查询数据库表字段具有某些特征的表

目录 引言举例总结 引言 当我们把一个项目做完以后&#xff0c;客户要求我们把系统中所有的电话&#xff0c;证件号等进行加密处理时&#xff0c;我们难道要一个表一表去查看那些字段是电话和证件号码吗&#xff1f; 这种办法有点费劲&#xff0c;下面我们来探索如何找到想要的…

【大数据】Flink 测试利器:DataGen

Flink 测试利器&#xff1a;DataGen 1.什么是 FlinkSQL &#xff1f;2.什么是 Connector &#xff1f;3.DataGen Connector3.1 Demo3.2 支持的类型3.3 连接器属性 4.DataGen 使用案例4.1 场景一&#xff1a;生成一亿条数据到 Hive 表4.2 场景二&#xff1a;持续每秒生产 10 万条…

进程间通信之匿名管道通信

每一次的努力都是自我成长的一步&#xff0c;坚持不懈的付出会铺就通向成功的道路。文章目录 进程间通信的介绍进程间通信的发展进程间通信的分类进程间通讯的本质资源&#xff1f;这个资源谁提供的&#xff1f; 管道什么是管道匿名管道管道小总结现在我给大家看一下管道通信的…

SCDN高防如何保护你的服务器

随着互联网的发展&#xff0c;如今的网络世界&#xff0c;虽说给我们的衣食住行带来了非常大的便利&#xff0c;但同时它存在着各种各样的威胁。比如我们的网站&#xff0c;如果不做任何保护措施的话&#xff0c;就很容易被DDoS、CC等攻击堵塞网络、窃取目标系统的信息&#xf…

这种网页要小心!注意你的账号密码泄露!

目录 H5是泄露账号和数据的重要渠道 代码混淆是最佳的安全保护手段 基于AI的自适应代码混淆 我们经常见到各类H5海报&#xff0c;产品展示、活动促销、招聘启事等。H5不仅能够无缝地嵌入App、小程序&#xff0c;还可以作为一个拥有独立链接地址的页面&#xff0c;直接在PC端打开…

AIOps案例 | 携手擎创,中邮信科成功打造新一代IT智能运维平台,收益明显!

为推动邮政信息科技体制改革、提升信息科技自主供给能力&#xff0c;在原信息技术局、数据中心和软开中心基础上&#xff0c;中邮信息科技&#xff08;北京&#xff09;有限公司(简称“中邮信科公司”)经中国邮政集团有限公司于2019年5月被批准成立。 公司主要负责邮政各类信息…

[论文阅读]DeepFusion

DeepFusion Lidar-Camera Deep Fusion for Multi-Modal 3D Object Detection 用于多模态 3D 物体检测的激光雷达相机深度融合 论文网址&#xff1a;DeepFusion 论文代码&#xff1a;DeepFusion 摘要 激光雷达和摄像头是关键传感器&#xff0c;可为自动驾驶中的 3D 检测提供补…

通过OpenIddict设计一个授权服务器03-客户凭证流程

在本部分中&#xff0c;我们将把 OpenIddict 添加到项目中&#xff0c;并实施第一个授权流程&#xff1a;客户端凭证流。 添加 OpenIddict 软件包 首先&#xff0c;我们需要安装 OpenIddict NuGet 软件包 dotnet add package OpenIddict dotnet add package OpenIddict.AspN…

Android CarService源码分析

文章目录 一、CarService的基本架构1.1、Android Automative整体框架1.2、Framework CarService1.3、目录结构1.3.1、CarService1.3.2、Car APP 二、CarService的启动流程2.1、系统启动后在SystemServer进程中启动CarServiceHelperService2.2、CarService启动 三、CarService源…

浅聊雷池社区版(WAF)的tengine

雷池社区版是一个开源的免费Web应用防火墙&#xff08;WAF&#xff09;&#xff0c;专为保护Web应用免受各种网络攻击而设计。基于强大的Tengine&#xff0c;雷池社区版提供了一系列先进的安全功能&#xff0c;适用于中小企业和个人用户。 Tengine的故事始于2011年&#xff0c;…

Android-三方框架的源码

ARouter Arouter的整体思路是moduelA通过中间人ARouter把路由信息的存到仓库WareHouse&#xff1b;moduleB发起路由时&#xff0c;再通过中间人ARouter从仓库WareHouse取出路由信息&#xff0c;这要就实现了没有依赖的两者之间的跳转与通信。其中涉及Activity的跳转、服务prov…

微信原生小程序上传与识别以及监听多个checkbox事件打开pdf

1.点击上传并识别 组件样式<van-field border"{{ false }}" placeholder"请输入银行卡卡号" model:value"{{bankNo}}" label"卡号"><van-icon bindtap"handleChooseImg" slot"right-icon" name"sca…

网工内推 | 运维工程师,最高10K*15薪,思科认证优先

01 乐歌股份 招聘岗位&#xff1a;服务器运维工程师 职责描述&#xff1a; 1、负责公司云上云下所有服务器的日常运维工作&#xff0c;包括应用部署、巡检、备份、日志、监控&#xff0c;故障处理&#xff0c;性能优化等&#xff0c;保障公司相关系统稳定运行。 2、为开发、测…