操作系统之进程管理——生产者&消费者实现线程同步&互斥【详细代码&原理解析】

在这里插入图片描述

一、实验目的与要求

熟悉Windows系统提供的线程创建与撤销等API系统调用,掌握Windows系统环境下线程的创建与撤销方法、掌握线程实现同步和互斥的实现原理和方法,了解在windows环境下是如何进行线程调度的。在这里插入图片描述


二、实验内容

1、熟悉开发环境Visual C++ 6.0;
2、Windows系统环境下线程的创建与撤销方法;
3、编程:在主线程中调用CreateThread( )创建子线程,并在子线程中显示类似“Thread is running !”等字样。
4、线程互斥的实现
5、线程同步的实现


三、背景知识

1、CreateThread函数
功能: 在调用进程的虚拟空间中创建一个线程。 函数原型:HANDLE WINAPI
CreateThread(LPSECURITY_ATTRIBUTES lpThreadAttributes,SIZE_T
dwStackSize,LPTHREAD_START_ROUTINE lpStartAddress,LPVOID
lpParameter,DWORD dwCreationFlags,LPDWORD lpThreadId)

参数描述:
lpThreadAttributes: 输入参数,可选。指向SECURITY_ATTRIBUTES类型的结构体指针,决定返回的线程句柄是否可以被子线程继承。如果该参数为NULL则使用默认安全性,不可以被子线程继承;否则需要定义一个SECURITY_ATTRIBUTES类型的结构体并将它的bInheritHandle成员初始化为TRUE。
dwStackSize: 输入参数。设置初始堆栈的大小,以字节为单位,如果为0,新线程默认使用与调用该函数的线程相同的栈空间大小。
lpStartAddress: 输入参数,指向线程函数的地址指针。定义线程函数格式如下:
(1)DWORD WINAPI 函数名(LPVOID lpParam)。
(2)如果使用void 函数名(LPVOID lpParam)进行定义,则lpStartAddress必须写成(LPTHREAD_START_ROUTINE)函数名。
lpParameter:输入参数。向线程函数传递参数的指针,不需传递参数时设为NULL。
dwCreationFlags:输入参数。控制线程创建的标志,取值如下:
(1)0:表示线程创建后立即运行。
(2)CREATE_SUSPENDED(0x00000004):创建一个挂起的线程,直到调用ResumeThread函数后开始运行。
(3)STACK_SIZE_PARAM_IS_A_RESERVATION(0x00010000):dwStackSize参数指定初始的保留堆栈的大小,如果没说明该参数,dwStackSize指定提交的大小。
lpThreadId:输出参数。DWORD类型变量的指针,用以保存新线程的id,如不需返回新线程的id则设为NULL。
返回值: 函数成功,返回线程句柄;函数失败返回false。
注意: 线程标识(ThreadId)和线程句柄(Handle)概念不同。线程标识是一个32位无符号整数,其值系统唯一,通常利用线程标识可以进一步获取线程的其它信息,不能控制线程。而线程句柄表面上看也是一个32位无符号整数且系统唯一,但它是一个内核对象的标识,通过它可以控制线程,属于系统资源,用完后一定要用CloseHandle()函数关闭,从而将该资源归还给系统。需要注意的是CloseHandel()只是关闭了一个线程句柄对象,表示不再使用该句柄对线程做任何控制了,并没有结束线程。

2、CreateMutex函数

功能: 创建一个有名或无名的互斥信号量对象。 函数原型:HANDLE CreateMutex(LPSECURITY_ATTRIBUTES lpMutexAttributes, BOOL bInitialOwner, LPCTSTR lpName);
lpMutexAttributes: 参见 CreateThread函数。
bInitialOwner: 输入参数。如创建进程希望立即拥有互斥体,则设为TRUE,否则设定为FALSE。
lpName: 指定互斥体对象的名字。用vbNullString创建一个未命名的互斥体对象。如已经存在拥有这个名字的一个事件,则打开现有的已命名互斥体。这个名字可能不与现有的事件、信号机、可等待计时器或文件映射相同。
例如: h_mutex1=CreateMutex(NULL,FALSE,“mutex_for_readcount”);

3、WaitForSingleObject函数

功能: 当指定对象存在信号量或超时返回。 函数原形:DWORD WaitForSingleObject(HANDLE
hHandle,DWORD dwMilliseconds);
hHandle: 对象句柄。
dwMilliseconds: 定时时间间隔,单位为milliseconds(毫秒)。如果指定一个非零值,函数处于等待状态直到hHandle标记的对象被触发,或者时间到了。如果dwMilliseconds为0,即便没有信号量,也会立即从函数返回;如果dwMilliseconds为INFINITE,对象被触发信号后,函数才会返回。

4、ReleaseMutex函数

功能: 释放互斥对象的控制权。一个线程释放了互斥对象的控制权后,如果其它进(线)程在等待互斥对象置位,则等待的进(线)程可以得到该互斥对象,从等待函数返回,互斥对象被新的进(线)程所拥有。
函数原型: BOOL WINAPI ReleaseMutex(HANDLE hMutex); hMutex:输入参数,制定一个互斥体的句柄。
返回值: TRUE表示成功,FALSE表示失败。

5、CreateSemaphore函数

功能: 创建或打开一个命名或未命名的信号量对象。 函数原形:HANDLE
CreateSemaphore(LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,LONG
lInitialCount,LONG lMaximumCount, LPCTSTRlpName);

参数描述:
lpSemaphoreAttributes: 输入参数,可选。指向SECURITY_ATTRIBUTES类型的结构体指针,决定返回的信号量句柄是否可以被子线程继承(参见创建线程)。
lInitialCount: 输入参数。设置信号量的初始计数,可设置零到lMaximumCount之间的任意一个值。大于0的值表示有信号,0表示无信号。每执行WaitForSingleObject函数一次,计数值就减一;每执行ReleaseSemaphore函数一次,信号量按照该函数说明的数量递增。
lMaximumCount: 输入参数。设置信号量的最大计数值(必须大于0)。
lpName: 输入参数。指定信号量对象的名称,如果该参数为Null则产生未命名的信号量对象;如果该参数非空,且系统中存在同名信号量对象,则该函数请求信号量的SEMAPHORE_ALL_ACCESS权限。
返回值: 如执行成功,返回信号量对象的句柄。零表示出错,会设置GetLastError。即使返回一个有效的句柄,但系统中存在同名的信号量,则GetLastError返回ERROR_ALREADY_EXISTS值。

6、ReleaseSemaphore函数

功能: 用于对指定的信号量增加指定的值。 函数原形:BOOL ReleaseSemaphore(HANDLE hSemaphore,LONG lReleaseCount,LPLONG lpPreviousCount);

参数描述:
hSemaphore: 输入参数。所要操作的信号量对象的句柄,该句柄是CreateSemaphore或者OpenSemaphore函数的返回值,并且必须有SEMAPHORE_MODIFY_STATE的访问权限。
lReleaseCount: 输入参数。信号量对象在当前基础上所要增加的值,这个值必须大于0。如果信号量的当前值加上lReleaseCount以后导致信号量的值大于信号量的最大值,那么信号量的当前值不变,同时函数返回FALSE。
lpPreviousCount: 输出参数。指向返回信号量上次值的变量的指针,如果不需要信号量上次的值,该参数可以设置为NULL。
返回值: 如果成功返回非0;失败返回0,可以调用GetLastError函数得到详细出错信息。


四、实验原理

1、线程互斥实现原理

打开VC++6.0开发环境,创建一个源文件,在主函数中利用CreateThread函数创建2个线程,创建后立即执行,每个线程执行10次,分别对共享变量R执行加1操作,R的初值为0,使用Sleep函数控制线程的执行速度。为了实现互斥使用R,需在主函数中,利用CreateMutex

创建一个互斥体,在访问R前利用WaitForSingleObject函数测试互斥体信号是否存在,如该信号存在则可以访问R并上锁(排斥其它线程访问R),使用完后利用ReleaseMutex函数释放资源并唤醒等待R资源而阻塞的线程;如该信号不存在,则该线程阻塞。

2、线程同步实现原理

打开VC++6.0开发环境,创建一个源文件,在主函数中利用CreateThread函数创建生产者线程和消费者线程,创建后立即执行,每个线程执行10次。其中生产者线程完成向共享缓冲区送数据,消费者线程负责从共享缓冲区取数据并进行进一步加工处理,缓冲区初始状态为空(生产者线程先执行)。为了正确实现两个线程的同步,需在主函数中利用CreateSemaphore函数创建缓冲区满“full”信号量(初值为0,表示不满)和缓冲区空信号量“empty”(初值为1,表示缓冲区空)。生产者线程送数前利用WaitForSingleObject函数测试empty信号是否存在,如该信号存在则可以往共享缓冲区送数,送完后ReleaseSemaphore函数释放一个full信号;如该信号不存在则说明缓冲区不空,生产者线程应阻塞。而消费者线程在取数前利用WaitForSingleObject函数测试full信号是否存在,如该信号存在则可以从共享缓冲区送数,取完后ReleaseSemaphore函数释放一个empty信号;如该信号不存在则说明缓冲区不满,消费者线程应阻塞。使用Sleep函数控制两个线程的执行速度。


五、程序代码

1.1、线程互斥源代码

#include <windows.h> 
#include <iostream>  
static int count=0; //共享变量
static HANDLE h1,h2;  //两个子线程的句柄变量
HANDLE g_hMutex;     //互斥信号量句柄,用于诸线程互斥访问缓冲区
using namespace std;
//子线程fun c2的实现
void func2()   
{
	int i,r2;
	for(i=0;i<10;i++)
	{
		Sleep(10);
		WaitForSingleObject(g_hMutex,INFINITE);//此处补齐代码;
		r2=count;    
		r2=r2+1;
		count=r2;
		cout<<"count in func2="<<count<<endl;
		ReleaseMutex(g_hMutex);//此处补齐代码;
	}
}    
//子线程func1的实现,使用互斥信号时,将Sleep语句移到前面
void func1()   
{       
	int r1,i;
	for(i=0; i<10; i++)
	{
		Sleep(30);
		WaitForSingleObject(g_hMutex,INFINITE);//此处补齐代码; 
		r1=count;
		r1=r1+1;
		count=r1;    
		cout<<"count in func1="<<count<<endl;
		ReleaseMutex(g_hMutex);//此处补齐代码; 
	} 
}
//主线程的实现
int main()
{ 
	DWORD  dwThreadID1, dwThreadID2;
	g_hMutex = CreateMutex(NULL,FALSE,"mutex-for-readcount");//此处补齐代码; //创建互斥信号量
	//创建子线程 func1
	h1=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)func1,NULL,0,&dwThreadID1);//此处补齐代码;
	cout<<"**********************"<<endl;
	cout<<"********23夏旭********"<<endl;  
	cout<<"**********************"<<endl; 
	if(h1==NULL) cout<<"Thread1 create Fail!"<<endl;
	else cout<<"Thread1 create success!"<<endl;
	//创建子线程 func2
	h2=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)func2,NULL,0,&dwThreadID2);//此处补齐代码;       
	if(h1==NULL) cout<<"Thread2 create Fail!"<<endl;
	else cout<<"Thread2 create success!"<<endl;
	Sleep(2000);
	CloseHandle(h1);
	CloseHandle(h2);
	CloseHandle(g_hMutex);
	ExitThread(0);    
}

1.2、线程互斥变式1源代码

#include <windows.h> 
#include <iostream>  
static int count=0; //共享变量
static HANDLE h1,h2;  //两个子线程的句柄变量
HANDLE g_hMutex;     //互斥信号量句柄,用于诸线程互斥访问缓冲区
using namespace std;
// 变式 1.0 
void func2()   
{
	int i,r2;
	for(i=0;i<10;i++)
	{
		r2=count;    
		r2=r2+1;
		Sleep(13);
		count=r2;
		cout<<"count in func2="<<count<<endl;
	}
}    
void func1()   
{       
	int r1,i;
	for(i=0;i<10;i++)
	{
		r1=count;
		r1=r1+1;
		Sleep(33);
		count=r1;    
		cout<<"count in func1="<<count<<endl;
	} 
}
//主线程的实现
int main()
{ 
	DWORD  dwThreadID1, dwThreadID2;
	g_hMutex = CreateMutex(NULL,FALSE,"mutex-for-readcount");//此处补齐代码; //创建互斥信号量
	//创建子线程 func1
	h1=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)func1,NULL,0,&dwThreadID1);//此处补齐代码;
	cout<<"**********************"<<endl;
	cout<<"********23夏旭********"<<endl;  
	cout<<"**********************"<<endl; 
	if(h1==NULL) cout<<"Thread1 create Fail!"<<endl;
	else cout<<"Thread1 create success!"<<endl;
	//创建子线程 func2
	h2=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)func2,NULL,0,&dwThreadID2);//此处补齐代码;       
	if(h1==NULL) cout<<"Thread2 create Fail!"<<endl;
	else cout<<"Thread2 create success!"<<endl;
	Sleep(2000);
	CloseHandle(h1);
	CloseHandle(h2);
	CloseHandle(g_hMutex);
	ExitThread(0);    
}

1.3、线程互斥变式2源代码

#include <windows.h> 
#include <iostream>  
static int count=0; //共享变量
static HANDLE h1,h2;  //两个子线程的句柄变量
HANDLE g_hMutex;     //互斥信号量句柄,用于诸线程互斥访问缓冲区
using namespace std;
//变式2.0 
void func2()   
{
	int i,r2;
	for(i=0;i<10;i++)
	{
		r2=count;    
		r2=r2+1;
		Sleep(3);
		count=r2;
		WaitForSingleObject(g_hMutex,INFINITE);
		cout<<"count in func2="<<count<<endl;
		ReleaseMutex(g_hMutex);
	}
}    
void func1()   
{       
	int r1,i;
	for(i=0;i<10;i++)
	{
		r1=count;
		r1=r1+1;
		Sleep(1);
		count=r1; 
		WaitForSingleObject(g_hMutex,INFINITE);
		cout<<"count in func1="<<count<<endl;
		ReleaseMutex(g_hMutex); //退出临界区
	} 
}
//主线程的实现
int main()
{ 
	DWORD  dwThreadID1, dwThreadID2;
	g_hMutex = CreateMutex(NULL,FALSE,"mutex-for-readcount");//此处补齐代码; //创建互斥信号量
	//创建子线程 func1
	h1=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)func1,NULL,0,&dwThreadID1);//此处补齐代码;
	cout<<"**********************"<<endl;
	cout<<"********23夏旭********"<<endl;  
	cout<<"**********************"<<endl; 
	if(h1==NULL) cout<<"Thread1 create Fail!"<<endl;
	else cout<<"Thread1 create success!"<<endl;
	//创建子线程 func2
	h2=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)func2,NULL,0,&dwThreadID2);//此处补齐代码;       
	if(h1==NULL) cout<<"Thread2 create Fail!"<<endl;
	else cout<<"Thread2 create success!"<<endl;
	Sleep(2000);
	CloseHandle(h1);
	CloseHandle(h2);
	CloseHandle(g_hMutex);
	ExitThread(0);    
}

2.1、线程同步源代码

#include <windows.h> 
#include <iostream>
using namespace std;
HANDLE hThread1; //子线程1的句柄
HANDLE hThread2;//子线程2的句柄
HANDLE hHandle=NULL; //主线程子线程通信信号量
HANDLE hHandle1=NULL; //信号量1的句柄,全局变量 
HANDLE hHandle2=NULL; //信号量2的句柄,全局变量     
DWORD WINAPI producer(LPVOID lpParam)  //生产者线程 
{   
	int i;
	for(i=0;i<10;i++)
	{
		WaitForSingleObject(hHandle2,INFINITE);//此处补齐代码;
		Sleep(10);
        cout<<"生产者线程在运行!"<<endl;
		ReleaseSemaphore(hHandle1,1,NULL);//此处补齐代码;
	}
	return 0;
}
DWORD WINAPI consumer(LPVOID lpParam)  //消费者线程
{   
	int i;
	for(i=0;i<10;i++)
	{
		WaitForSingleObject(hHandle1,INFINITE);//此处补齐代码;
		Sleep(10);
        cout<<"消费者线程在运行!"<<endl;
		ReleaseSemaphore(hHandle2,1,NULL);//此处补齐代码;
	}
	ReleaseSemaphore(hHandle,1,NULL);//此处补齐代码;  //唤醒主线程
	return 0;
}
int main()
{  
    DWORD dwThreadID1,dwThreadID2;
    hHandle=CreateSemaphore(NULL,0,10,NULL);//此处补齐代码; //Semaphore,初值为0
    cout<<"****************************"<<endl;
	cout<<"***********23夏旭***********"<<endl;  
	cout<<"****************************"<<endl; 
    hHandle1=CreateSemaphore(NULL,0,10,NULL);//此处补齐代码; //创建full信号量,初值为0
    if(hHandle1==NULL)  cout<<"full Semaphore Create Fail!"<<endl;
    else  cout<<"full Semaphore Create Success!"<<endl;
    hHandle2=CreateSemaphore(NULL,1,10,NULL);//此处补齐代码; //创建empty信号量,初值为1
    if(hHandle2==NULL)  cout<<"empyt Semaphore Create Fail!"<<endl;
    else  cout<<"empyt Semaphore Create Success!"<<endl; 
    hThread1=CreateThread(NULL,0,producer,NULL,0,NULL);//此处补齐代码;    //创建子线程1
    if(hThread1==NULL)  cout<<"producer create Fail!"<<endl; 
    else  cout<<"producer create Success!"<<endl; 
    hThread2=CreateThread(NULL,0,consumer,NULL,0,NULL);//此处补齐代码;    //创建子线程2
    if(hThread1==NULL)	cout<<"consumer create Fail!"<<endl; 
    else  cout<<"consumer create Success!"<<endl; 
    WaitForSingleObject(hHandle,INFINITE);//此处补齐代码; //主线程等待子线程结束
    cout<<"主函数运行!"<<endl;
    //以下关闭函数 
    CloseHandle(hHandle);
    CloseHandle(hHandle1);
    CloseHandle(hHandle2);
    CloseHandle(hThread1);
    CloseHandle(hThread2);     
    return 0;   
}

2.2、线程同步变式1源代码

#include <windows.h> 
#include <iostream>
using namespace std;
HANDLE hThread1; //子线程1的句柄
HANDLE hThread2;//子线程2的句柄
HANDLE hHandle=NULL; //主线程子线程通信信号量
HANDLE hHandle1=NULL; //信号量1的句柄,全局变量 
HANDLE hHandle2=NULL; //信号量2的句柄,全局变量     
DWORD WINAPI producer(LPVOID lpParam)  //生产者线程 
{   
	int i;
	for(i=0;i<10;i++)
	{
		WaitForSingleObject(hHandle2,INFINITE);//此处补齐代码;
		Sleep(10);
        cout<<"生产者线程在运行!"<<endl;
		ReleaseSemaphore(hHandle1,1,NULL);//此处补齐代码;
	}
	return 0;
}
DWORD WINAPI consumer(LPVOID lpParam)  //消费者线程
{   
	int i;
	for(i=0;i<10;i++)
	{
		WaitForSingleObject(hHandle1,INFINITE);//此处补齐代码;
		Sleep(10);
        cout<<"消费者线程在运行!"<<endl;
		ReleaseSemaphore(hHandle2,1,NULL);//此处补齐代码;
	}
	ReleaseSemaphore(hHandle,1,NULL);//此处补齐代码;  //唤醒主线程
	return 0;
}
int main()
{  
    DWORD dwThreadID1, dwThreadID2;
	hHandle=CreateSemaphore(NULL,0,1,NULL);//此处补齐代码; //Semaphore,初值为0

	hHandle1=CreateSemaphore(NULL,1,1,NULL);//此处补齐代码; //创建full信号量,初值为0
    cout<<"****************************"<<endl;
	cout<<"***********23夏旭***********"<<endl;  
	cout<<"****************************"<<endl; 
	if(hHandle1==NULL)  
		cout<<"full Semaphore Create Fail!"<<endl;
	else 
		cout<<"full Semaphore Create Success!"<<endl;
	hHandle2=CreateSemaphore(NULL,0,1,NULL);//此处补齐代码; //创建empty信号量,初值为1
	if(hHandle2==NULL)  
		cout<<"empyt Semaphore Create Fail!"<<endl;
	else 
		cout<<"empyt Semaphore Create Success!"<<endl; 
	hThread1=CreateThread((LPSECURITY_ATTRIBUTES)NULL,0,(LPTHREAD_START_ROUTINE)producer, (LPVOID)NULL,0,&dwThreadID1);//此处补齐代码;    //创建子线程1
	if(hThread1==NULL)
		cout<<"producer create Fail!"<<endl; 
	else 
		cout<<"producer create Success!"<<endl; 
	hThread2=CreateThread((LPSECURITY_ATTRIBUTES)NULL,0,(LPTHREAD_START_ROUTINE)consumer, (LPVOID)NULL,0,&dwThreadID2);//此处补齐代码;    //创建子线程2
	if(hThread1==NULL)
		cout<<"consumer create Fail!"<<endl; 
	else 
		cout<<"consumer create Success!"<<endl; 
	WaitForSingleObject(hHandle,INFINITE);//此处补齐代码; //主线程等待子线程结束
	cout<<"主函数运行!"<<endl;
	//以下关闭函数 
	CloseHandle(hHandle);
	CloseHandle(hHandle1);
	CloseHandle(hHandle2);
	CloseHandle(hThread1);
	CloseHandle(hThread2);    
	return 0;   
}

2.3、线程同步变式2源代码

#include <windows.h> 
#include <iostream>
using namespace std;
HANDLE hThread1; //子线程1的句柄
HANDLE hThread2;//子线程2的句柄
HANDLE hHandle=NULL; //主线程子线程通信信号量
HANDLE hHandle1=NULL; //信号量1的句柄,全局变量 
HANDLE hHandle2=NULL; //信号量2的句柄,全局变量     
DWORD WINAPI producer(LPVOID lpParam)  //生产者线程 
{   
	int i;
	for(i=0;i<10;i++)
	{
		WaitForSingleObject(hHandle2,INFINITE);//此处补齐代码;
		Sleep(10);
        cout<<"生产者线程在运行!"<<endl;
		ReleaseSemaphore(hHandle1,1,NULL);//此处补齐代码;
	}
	ReleaseSemaphore(hHandle,1,NULL);
	return 0;
}
DWORD WINAPI consumer(LPVOID lpParam)  //消费者线程
{   
	int i;
	for(i=0;i<10;i++)
	{
		WaitForSingleObject(hHandle1,INFINITE);//此处补齐代码;
		Sleep(20);
        cout<<"消费者线程在运行!"<<endl;
		ReleaseSemaphore(hHandle2,1,NULL);//此处补齐代码;
	}
	//此处补齐代码;  //唤醒主线程
	return 0;
}
int main()
{  
	DWORD dwThreadID1, dwThreadID2;
	hHandle=CreateSemaphore(NULL,0,1,NULL);//此处补齐代码; //Semaphore,初值为0
	hHandle1=CreateSemaphore(NULL,0,5,NULL);//此处补齐代码; //创建full信号量,初值为0
    cout<<"****************************"<<endl;
	cout<<"***********23夏旭***********"<<endl;  
	cout<<"****************************"<<endl; 
	if(hHandle1==NULL)  
		cout<<"full Semaphore Create Fail!"<<endl;
	else 
		cout<<"full Semaphore Create Success!"<<endl;
	hHandle2=CreateSemaphore(NULL,5,5,NULL);//此处补齐代码; //创建empty信号量,初值为1
	if(hHandle2==NULL)  
		cout<<"empyt Semaphore Create Fail!"<<endl;
	else 
		cout<<"empyt Semaphore Create Success!"<<endl; 
	hThread1=CreateThread((LPSECURITY_ATTRIBUTES)NULL,0,(LPTHREAD_START_ROUTINE)producer, (LPVOID)NULL,0,&dwThreadID1);//此处补齐代码;    //创建子线程1
	if(hThread1==NULL)
		cout<<"producer create Fail!"<<endl; 
	else 
		cout<<"producer create Success!"<<endl; 
	hThread2=CreateThread((LPSECURITY_ATTRIBUTES)NULL,0,(LPTHREAD_START_ROUTINE)consumer, (LPVOID)NULL,0,&dwThreadID2);//此处补齐代码;    //创建子线程2
	if(hThread1==NULL)
		cout<<"consumer create Fail!"<<endl; 
	else 
		cout<<"consumer create Success!"<<endl; 
	WaitForSingleObject(hHandle,INFINITE);//此处补齐代码; //主线程等待子线程结束
	cout<<"主函数运行!"<<endl;
	//以下关闭函数 
	CloseHandle(hHandle);
	CloseHandle(hHandle1);
	CloseHandle(hHandle2);
	CloseHandle(hThread1);
	CloseHandle(hThread2);    
	return 0;   
}

六、结果分析

1.1线程互斥----运行结果图与结果分析

在这里插入图片描述

创建两个子线程实现进程的互斥,分别对两个子线程给与10次访问共享变量的次数,每次对count+1,当互斥实现时,那么count的最终值为20两个线程分别对count修改10次。那么互斥的实现关键在于互斥体(互斥信号量)的创建,两个子线程在访问count是都要通过WaitForSingleObject函数来确认互斥信号是否存在,如果存在才可以对count进行操作同时排斥其他线程访问R,当次线程的操作结束后释放互斥信号(ReleaseSemaphore函数),那么这时候才允许别的线程访问count。


1.2线程互斥变式1----运行结果图与结果分析

在这里插入图片描述

变式1在func( )函数中没有wait原语和signal原语,此时count不在是一个共享变量,它可以同时被线程1和线程2进行修改,那么此时线程1和线程2在修改count是获得的count的值就会受到间隔时间的影响,线程1间隔为29毫秒线程2间隔为12毫秒,我们可以看上图中正好出现一个我们需要的数值,那就是线程二在执行第十次的时候返回值为5,真是因为当线程一执行完将count修改为4的时候线程2获得了count为4的值再进行修改就变为5,所以此时两个线程并不互斥的使用变量count。

即:在还没有实现一个语句的完整输出时,就被sleep( )函数打断,开始输出另一个语句了,在输出完另一个语句以后,继续输出之前被打断并未完整输出的。这是因为显示器是一个独占资源,在实现过程中要互斥,而程序既没有对屏幕互斥,也没有对count进行互斥,2个线程交织了。


1.3线程互斥变式2----运行结果图与结果分析

在这里插入图片描述
这个跟第二个相似,count不互斥,输出结果互斥,也就是“输出都是完整的,但是count的值是错误的”。它把WaitForSingleObject函数和ReleaseSemaphore函数之间只放了输出,所以输出互斥结果不互斥。除此之外,改变sleep( )函数中的值的时候,输出会发生改变。


2.1线程同步----运行结果图与结果分析

在这里插入图片描述
在生产者线程函数DWORD WINAPI producer(LPVOID lpParam)和消费者线程函数DWORD WINAPI consumer(LPVOID lpParam)中,主语句放在wait原语和signal原语之间,这是正确的做法。在创建缓冲区时的初始值为0,最大为1,所以同步实现时是生产者与消费者交替10次并且生产者先行。hHandle1和hHandle2两个信号量分别代表full和empty信号量,但我们需要给hHandle1一个信号量,保证首先运行生产者线程在运行!之后创建两个线程hThread1和hThread2,让两线程同步执行。之后便会出现上图打印情况。最终在打印结束后会唤醒主线程,结束程序。


2.2线程同步变式1----运行结果图与结果分析

在这里插入图片描述
初步修改之后,消费者进程能够在生产者进程之前实现了,但是在结束之前,第10个生产者进程没有来得及实现,这时,只要把原语移动一下位置就可以实现了。当前的生产者进程执行次数为10次,如不是我们可以让hHandle1信号量为1并且每次hHandle1、hHandle2的信号量增1就可以了,在消费者消费了hHandle1的1之后就会去hHandle2自加1,之后hHandle2自加1就会形成,先执行消费者,再执行生产者,一直持续10次。


2.3线程同步变式2-----运行结果图与结果分析

在这里插入图片描述
这里设置了5个缓冲区且初始设置为空并且改变了2个进程的速度当生产者将缓冲区填满后消费者才开始取出,又因为生产者速度快所以后面交替进行,当生产者结束后消费者才连续执行。也就是先让hHandle1为0,从而让生产者先执行,在生产者执行完毕后让hHandle1自增4(只要不是1就行),这样,消费者可以多执行几次


  • 17
    点赞
  • 61
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
实验目的 (1)掌握Windows系统提供的线程创建与撤销系统调用 (2)掌握Windows系统环境下线程的创建与撤销方法 2 实验准备知识 (1)线程创建 CreateThread()完成线程的创建。它在调用进程的地址空间上创建一个线程,执行指定的函数,并返回新建立线程的句柄。 原型: HANDLE CreateThread(   LPSECURITY_ATTRIBUTES lpThreadAttributes,   DWORD dwStackSize,   LPTHREAD_START_ROUTINE lpStartAddress,   LPVOID lpParameter,   DWORD dwCreationFlags,   LPDWORD lpThreadId);   参数说明:   lpThreadAttributes:指向SECURITY_ATTRIBUTES型态的结构的指针。在Windows 98中忽略该参数。在Windows NT中,它被设为NULL,表示使用缺省值。   dwStackSize,线程堆栈大小,一般=0,在任何情况下,Windows根据需要动态延长堆栈的大小。   lpStartAddress,指向线程函数的指针,形式:@函数名,函数名称没有限制,但是必须以下列形式声明:   DWORD WINAPI ThreadProc (LPVOID pParam) ,格式不正确将无法调用成功。   lpParameter:向线程函数传递的参数,是一个指向结构的指针,不需传递参数时,为NULL。   dwCreationFlags :线程标志,可取值如下   CREATE_SUSPENDED: 创建一个挂起的线程   0 :创建后立即激活。   lpThreadId:保存新线程的id。   返回值:   函数成功,返回线程句柄;函数失败返回false。 (2)撤销线程 ExitThread()用于撤销当前线程 原型: VOID ExitThread( DWORD dwExitCode ); 参数说明: DwExitCode:指定线程返回码 返回值: 该函数没有返回值 用法举例: ExitThread(0); (3)挂起线程 Sleep()用于挂起当前正在执行的线程 原型: VOID Sleep( DWORD dwMilliseconds ); 参数说明: dwMilliseconds:指定挂起时间,单位为ms(毫秒)。 返回值: 该函数没有返回值。 (4)关闭句柄 函数CloseHandle()用于关闭已打开对象的句柄,其作用与释放动态申请的内存空间类似,这样可以释放系统资源,使进程安全运行。 原型: BOOL CloseHandle( HANDLE hObject ); 参数说明: hObject:已打开对象的句柄。 返回值: 成功,返回值为非0值;失败,则返回值为0.
实验一 linux 内核编译及添加系统调用 设计目的 Linux 是开源操作系统,用户可以根据自身系统需要裁剪、修改内核,定制出功能更加 合适、运行效率更高的系统,因此,编译 linux 内核是进行内核开发的必要基本功。 在系统中根据需要添加新的系统调用是修改内核的一种常用手段,通过本次实验,读 者应理解 linux 系统处理系统调用的流程以及增加系统调用的方法。 内容要求 (1) 添加一个系统调用,实现对指定进程的 nice 值的修改或读取功能,并返回进程最 新的 nice 值及优先级 prio。建议调用原型为: int mysetnice(pid_t pid, int flag, int nicevalue, void __user * prio, void __user * nice); 参数含义: pid:进程 ID。 flag:若值为 0,表示读取 nice 值;若值为 1,表示修改 nice 值。 Prio、nice:进程当前优先级及 nice 值。 返回值:系统调用成功时返回 0,失败时返回错误码 EFAULT。 (2) 写一个简单的应用程序测试(1)中添加的系统调用。 (3) 若程序中调用了 linux 的内核函数,要求深入阅读相关函数源码。 实验二 linux 内核模块编程 设计目的 Linux 提供的模块机制能动态扩充 linux 功能而无需重新编译内核,已经广泛应用在 linux 内核的许多功能的实现中。在本实验中将学习模块的基本概念、原理实现技术,然后利 用内核模块编程访问进程的基本信息,从而加深对进程概念的理解、对模块编程技术的掌 握。 内容要求 (1) 设计一个模块,要求列出系统中所有内核线程的程序名、PID 号、进程状态及 进程优先级。 (2) 设计一个带参数的模块,其参数为某个进程的 PID 号,该模块的功能是列出该 进程的家族信息,包括父进程、兄弟进程和子进程的程序名、PID 号。 (3) 请根据自身情况,进一步阅读分析程序中用到的相关内核函数的源码实现。 实验四 linux 进程管理 设计目的 (1) 熟悉 linux 的命令接口。 (2) 通过对 linux 进程控制的相关系统调用的编程应用,进一步加深对进程概念的理解, 明确进程和程序的联系和区别,理解进程并发执行的具体含义。 (3) 通过 Linux 管道通信机制、消息队列通信机制、共享内存通信机制的使用,加深 对不同类型的进程通信方式的理解。 (4) 通过对 linux 的 Posix 信号量的应用,加深对信号量同步机制的理解。 (5)请根据自身情况,进一步阅读分析相关系统调用的内核源码实现。 设计内容 (1)熟悉 linux 常用命令:pwd,useradd,passwd, who, ps, pstree, kill, top, ls, cd, mkdir, rmdir, cp, rm, mv, cat, more, grep 等。 (2) 实现一个模拟的 shell: 编写三个不同的程序 cmd1.c,cmd2.c,cmd3.c,每个程序的功能自定,分别编译成可执 行文件 cmd1,cmd2,cmd3。然后再编写一个程序,模拟 shell 程序的功能,能根据用户输 入的字符串(表示相应的命令名),去为相应的命令创建子进程并让它去执行相应的程序,而父进程则等待子进程结束,然后再等待接收下一条命令。如果接收到的命令为 exit,则父 进程结束;如果接收到的命令是无效命令,则显示“Command not found”,继续等待。 (3) 实现一个管道通信程序: 由父进程创建一个管道,然后再创建 3 个子进程,并由这三个子进程利用管道与父进程 之间进行通信:子进程发送信息,父进程等三个子进程全部发完消息后再接收信息。通信的 具体内容可根据自己的需要随意设计,要求能试验阻塞型读写过程中的各种情况,测试管道 的默认大小,并且要求利用 Posix 信号量机制实现进程间对管道的互斥访问。运行程序,观 察各种情况下,进程实际读写的字节数以及进程阻塞唤醒的情况。 (4) 利用 linux 的消息队列通信机制实现两个线程间的通信: 编写程序创建两个线程:sender 线程和 receive 线程,其中 sender 线程运行函数 sender(), 它创建一个消息队列,然后,循环等待用户通过终端输入一串字符,将这串字符通过消息队 列发送给 receiver 线程,直到用户输入“exit”为止;最后,它向 receiver 线程发送消息“end”, 并且等待 receiver 的应答,等到应答消息后,将接收到的应答信息显示在终端屏幕上,删除 相关消息队列,结束程序的运行。Receiver 线程运行 rece
多线程生产者消费者问题是一个经典的并发编程问题,主要涉及到线程间的同步互斥。在这个问题中,有一个生产者线程和一个消费者线程,它们共享一个缓冲区。生产者线程负责往缓冲区中生产数据,而消费者线程则负责从缓冲区中消费数据。为了保证线程安全,需要使用锁或信号量等机制来实现线程间的同步互斥。 下面是一个简单的多线程生产者消费者的示例代码: ```python import threading import time # 缓冲区大小 BUFFER_SIZE = 10 # 生产者线程 class ProducerThread(threading.Thread): def __init__(self, buffer): threading.Thread.__init__(self) self.buffer = buffer def run(self): for i in range(20): # 生产数据 data = "data-%d" % i # 加锁 self.buffer.lock.acquire() # 如果缓冲区已满,等待消费者消费数据 while len(self.buffer.data) == BUFFER_SIZE: self.buffer.full.wait() # 将数据放入缓冲区 self.buffer.data.append(data) print("ProducerThread: produce %s" % data) # 释放锁 self.buffer.lock.release() # 通知消费者可以消费数据了 self.buffer.empty.notify() # 消费者线程 class ConsumerThread(threading.Thread): def __init__(self, buffer): threading.Thread.__init__(self) self.buffer = buffer def run(self): for i in range(20): # 加锁 self.buffer.lock.acquire() # 如果缓冲区为空,等待生产者生产数据 while len(self.buffer.data) == 0: self.buffer.empty.wait() # 从缓冲区中取出数据 data = self.buffer.data.pop(0) print("ConsumerThread: consume %s" % data) # 释放锁 self.buffer.lock.release() # 通知生产者可以生产数据了 self.buffer.full.notify() # 缓冲区 class Buffer: def __init__(self): self.data = [] self.lock = threading.Lock() self.full = threading.Condition(self.lock) self.empty = threading.Condition(self.lock) if __name__ == "__main__": buffer = Buffer() producer_thread = ProducerThread(buffer) consumer_thread = ConsumerThread(buffer) producer_thread.start() consumer_thread.start() producer_thread.join() consumer_thread.join() ``` 在上面的代码中,我们使用了一个列表来作为缓冲区,使用了 threading.Lock 来实现线程间的互斥,使用了 threading.Condition 来实现线程间的同步。具体来说,我们使用了 full 条件变量来表示缓冲区已满,empty 条件变量来表示缓冲区为空。当生产者线程往缓冲区中生产数据时,如果缓冲区已满,则调用 full.wait() 来等待消费者线程消费数据;当消费者线程从缓冲区中消费数据时,如果缓冲区为空,则调用 empty.wait() 来等待生产者线程生产数据。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

米莱虾

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值