操作系统原理实验二(三)

继续完成操作系统原理的实验

4.5(实验目的:熟悉Window线程创建过程)在windows环境下,利用高级语言编程环境(限定为VS环境或VC环境)调用CreateThread函数实现“并发地画圆和画方”。圆的中心,半径,颜色,正方形的中心,边长,颜色等参数自己确定,合适就行。圆和正方形的边界上建议取720个点。为直观展示绘制的过程,每个点绘制后睡眠0.2秒~0.5秒。
4.7(实验目的:理解“生产者-消费者”同步模型,熟悉Window同步控制机制和编程应用)在windows环境下,利用高级语言编程环境(限定为VS环境或VC环境)调用CreateThread函数和相关的同步函数,模拟实现“生产者-消费者”问题。

4.1Window进程创建,4.2Linux fork创建进程–>操作系统原理实验二(一)
4.3Linux 创建线程,4.4Window线程创建–>操作系统原理实验二(二)

4.5 Window线程创建

编译环境

1. VS2019
2. easyx图形库

运行效果

操作系统原理实验二(三)_第1张图片
操作系统原理实验二(三)_第2张图片
操作系统原理实验二(三)_第3张图片
操作系统原理实验二(三)_第4张图片

代码展示

#include
#include
#include
#include
#define pi  3.1415926535897932

//定义画方的线程
DWORD WINAPI drawSquare(LPVOID)
{
	//分为720个点
	for (int i = 0; i < 180; i++)
	{
		putpixel(50 + i, 50, RED);
		Sleep(50);
	}
	for (int i = 0; i < 180; i++)
	{
		putpixel(50 + 180, 50 + i, RED);
		Sleep(50);
	}
	for (int i = 0; i < 180; i++)
	{
		putpixel(50 + 180 - i, 50 + 180, RED);
		Sleep(50);
	}
	for (int i = 0; i < 180; i++)
	{
		putpixel(50, 50 + 180 - i, RED);
		Sleep(50);
	}
	return 0;
}

//定义画圆的函数
DWORD WINAPI drawCircle(LPVOID)
{
	//同样分为720个点
	for (int i = 0; i < 720; i++)
	{
		putpixel(350+100*cos(-pi/2+(double)((i*pi)/360)), 140 + 100*sin(-pi / 2 + (double)((i*pi)/360)), RED);
		Sleep(50);
	}
	return 0;
}
int main()
{
	// 初始化图形模式
	initgraph(640, 480);
	
	HANDLE hThread[2];//记录新线程句柄
	DWORD threadID;//记录线程ID
	if ((hThread[0] = CreateThread(NULL, 0, drawSquare, 0, 0, &threadID)) == NULL)
	{
		printf("线程创建失败!");
	}
	if ((hThread[1] = CreateThread(NULL, 0, drawCircle, 0, 0, &threadID)) == NULL)
	{
		printf("线程创建失败!");
	}
	
	//等待所有线程结束
	WaitForMultipleObjects(2, hThread, TRUE, INFINITE);

	CloseHandle(hThread[0]);
	CloseHandle(hThread[1]);
	
	getchar();
	// 关闭图形模式
	closegraph();
	return 0;
}

解释一下

#include 引入Easyx图形库

putpixel(int x,int y)函数是在图形窗口画一个点

需要注意的是它的传入参数是两个整数,而圆上的点我们又是通过三角函数算出来的,因此在画圆时不可避免的有一点误差

putpixel(350+100*cos(-pi/2+(double)((i*pi)/360)), 140 + 100*sin(-pi / 2 + (double)((i*pi)/360)), RED)中
cos(-pi/2+(double)((i*pi)/360))的-pi/2是使画点从最上方开始,而(double)((i*pi)/360))则是在将度数转换为弧度制时尽量保留精度

CreateThread()函数介绍:windows多线程(一) 创建线程 CreateThread

4.7 “生产者-消费者”同步模型

运行效果

操作系统原理实验二(三)_第5张图片

代码展示

#include
#include

#define P(S)  WaitForSingleObject(S,INFINITE) //定义Windows下的P操作
#define V(S)  ReleaseSemaphore(S,1,NULL)	  //定义Windows下的V操作


//预设2个生产者,2个消费者
//缓存区大小为3
#define num_of_productors 2
#define num_of_consumers 2
#define num_of_buffers 3

int nextp = 1;						//下一个产品编号  我们设定生产完12个产品就退出线程
int in = 0, out = 0;				//分别指向下一个存放产品和取出产品的缓冲区编号
int buf[num_of_buffers] = { 0 };	//缓冲区初始值为空

HANDLE empty, full;			//信号量
HANDLE mutex;				//互斥量
									

//定义线程 生产者
DWORD WINAPI producer(LPVOID pM)
{
	while (true)
	{
		int id = *((int*)(pM));	//生产者编号
		Sleep(100);
		P(empty);
		P(mutex);

		//存储操作
		Sleep(100);
		buf[in] = nextp;
		printf("生产者-%d 将数据-%d 放入缓冲区%d\n", id, nextp, in);
		nextp++;//已生产的数目++
		
		in = (in + 1) % num_of_buffers;//取得下次存入的标号

		V(mutex);
		V(full);

		if (nextp > 12)
			break;//跳出循环
	}
	return 0;
}
DWORD WINAPI consumer(LPVOID pM)
{
	while (true)
	{
		int id = *((int*)(pM));	//消费者编号
		Sleep(100);
		P(full);
		P(mutex);

		//取出操作
		Sleep(100);
		int num = buf[out];
		buf[out] = 0;
		printf("\t\t\t\t\t\t消费者-%d 将数据-%d 取出缓冲区%d\n", id, num, out);
		out = (out + 1) % num_of_buffers;//取得下次要取出的标号

		V(mutex);
		V(empty);
		
		if (nextp > 12)
			break;//跳出循环
	}
	return 0;
}
int main()
{
	printf("\t\t\t生产者消费者问题:%d生产者 %d消费者 %d缓冲区\n\n", num_of_productors, num_of_consumers, num_of_buffers);

	//创建信号量 empty  初值为num_of_buffers
	//参数分别表示  继承/初始化信号值/最大信号值/名称
	empty = CreateSemaphore(NULL, num_of_buffers, num_of_buffers, NULL);
	//创建信号量 full  初值为0
	full = CreateSemaphore(NULL, 0, num_of_buffers, NULL);
	//创建互斥量 mutex
	mutex = CreateSemaphore(NULL, 1, 1, NULL);

	//线程数
	const int num_of_thread = num_of_consumers + num_of_productors;
	HANDLE hThread[num_of_thread];

	//创建生产者线程
	int pID[2];
	for (int i = 0; i < num_of_productors; i++)
	{
		pID[i]= i + 1;
		//参数: /继承/初始栈大小/线程函数/向线程传递的参数/线程标志 0 表示创建后立即激活/保存新线程的ID
		if ((hThread[i] = CreateThread(NULL, 0, producer, (LPVOID*)&pID[i], 0, NULL)) == NULL)
		{
			printf("Create producer_thread failed...");
			exit(1);
		}
	}
	//创建消费者线程
	int cID[2];
	for (int i = 0; i < num_of_consumers; i++)
	{
		cID[i] = i + 1;
		if ((hThread[i + num_of_productors] = CreateThread(NULL, 0, consumer, (LPVOID*)&cID[i], 0, NULL)) == NULL)
		{
			printf("Create consumer_thread failed...");
			exit(1);
		}
	}
	//等待线程退出
	WaitForMultipleObjects(num_of_thread, hThread, TRUE, INFINITE);
	
	getchar();
	return 0;
}

WaitForSingleObject()函数会等待Object被标为有信号(signaled)时才返回。

参数:
分别是THandle和Timeout(毫秒单位)。

如果想要等待一条线程,那么你需要指定线程的Handle,以及相应的Timeout时间。当然,如果你想无限等待下去,Timeout参数可以指定系统常量INFINITE。在我们代码里就是用了无限等待时间。
它在等待的过程中会进入一个非常高效沉睡状态,只占用极少的CPU时间片。(它是在内核状态下等待内核对象,不切换到用户模式下,因而效率很高)

WaitForSingleObject()函数详细介绍可以点这里: WaitForSingleObject()函数、WaitForMultipleObject()函数

ReleaseSemaphore(HANDLE hSemaphore,
LONG lReleaseCount,
LPLONG lpPreviousCount)函数
,用于对指定的信号量增加指定的值。

参数:
1.hSemaphore- - ->所要操作的信号量对象的句柄,这个句柄是CreateSemaphore或者OpenSemaphore函数的返回值。这个句柄必须有SEMAPHORE_MODIFY_STATE 的权限。
2.lReleaseCount- - ->这个信号量对象在当前基础上所要增加的值,这个值必须大于0,如果信号量加上这个值会导致信号量的当前值大于信号量创建时指定的最大值,那么这个信号量的当前值不变,同时这个函数返回FALSE;
3.lpPreviousCount- - ->指向返回信号量上次值的变量的指针,如果不需要信号量上次的值,那么这个参数可以设置为NULL;返回值:如果成功返回TRUE,如果失败返回FALSE,可以调用GetLastError函数得到详细出错信息;

本题参考一位大佬的博客做了一些小修改,上链接:windows下 c 实现生产者消费者问题

写在结尾

希望以上可以帮到你!
如有错误,或不同想法,欢迎指出,互相学习共同进步!

你可能感兴趣的:(操作系统原理实验二(三))