首页 > 代码库 > 多线程实现生产者消费者问题 详细注释 事件+临界区 信号量+临界区2种方法

多线程实现生产者消费者问题 详细注释 事件+临界区 信号量+临界区2种方法

生产者消费者问题:  该问题描述了两个共享固定大小缓冲区的线程——即所谓的“生产者”和“消费者”——在实际运行时会发生的问题。生产者的主要作用是生成一定量的数据放到缓冲区中,然后重复此过程。与此同时,消费者也在缓冲区消耗这些数据。该问题的关键就是要保证生产者不会在缓冲区满时加入数据,消费者也不会在缓冲区中空时消耗数据。具体我就不解释了   应该都懂 不懂请百度一下

我是用事件实现生产者消费者问题的同步  用临界区实现互斥    我的这个做法与经典做法不同 即用信号量的做法!   经典做法为法二

法一:

具体看代码:

#include <stdio.h>
#include <Windows.h>
#include <process.h>
int vol = 2;//缓冲区容量
int *g_pBuffer, g_BufferOff = 0, count;//依次为缓冲区,指向当前产品存放的位置, 目前缓冲区中产品数量
HANDLE g_ProducerEvent, g_ConsumerEvent;
CRITICAL_SECTION g_cs;//临界区
int ProNum = 10;//要生产的产品数量
int HasProduced = 0;//已经生产的个数
int HasConsumerd = 0;//已经消费的个数

unsigned int __stdcall FunPro(PVOID Pv)//生产者
{
	int k = *(int *)Pv;//生产者编号
	while(1)
	{
		WaitForSingleObject(g_ProducerEvent,  INFINITE);//等待生产者获取运行权限
		EnterCriticalSection(&g_cs);//申请临界区
		if(count < vol)//缓冲区未满
		{
			if(count == 0)//如果当前有0个产品
			{
				SetEvent(g_ProducerEvent);//让生产者运行
				ResetEvent(g_ConsumerEvent);//阻止消费者运行 
			}	
			else
			{
				SetEvent(g_ProducerEvent);//如果有多个产品 且缓冲区没满
				SetEvent(g_ConsumerEvent);	//让2者均可以运行
			}
			if(HasProduced >= ProNum)//生产的产品个数已经达到就不再生产了
			{	
				LeaveCriticalSection(&g_cs);
				break; 
			}
			count++;//生产了一个产品 当前产品数加1
			HasProduced++;//已经生产国多少个产品了 包括已经消费的
			g_BufferOff = (g_BufferOff + 1) % vol;//当前生产的产品要存储的位置
			g_pBuffer[g_BufferOff] = HasProduced;
			printf("生产者 %d 生产出一个产品,编号为 %d 存储位置%d 共有产品%d个 \n",k, HasProduced, g_BufferOff, count);
			Sleep(10);
			
		}
		else//缓冲区已满
		{
			ResetEvent(g_ProducerEvent);// 阻止生产者运行
			SetEvent(g_ConsumerEvent);//  让消费者运行
			Sleep(10);//
		}
		LeaveCriticalSection(&g_cs);//释放临界区
		
	}
	return 0;
}

unsigned int __stdcall FunCon(PVOID Pv)
{
	int k = *(int *)Pv;//消费者编号
    while(1)
	{
		WaitForSingleObject(g_ConsumerEvent,  INFINITE);//等待消费者获取运行权限
		EnterCriticalSection(&g_cs);//进入临界区	
		if(count > 0)//如果当前有产品
		{
			if(count == vol)//如果缓冲区已满
			{
				ResetEvent(g_ProducerEvent);//让生产者不再运行
				SetEvent(g_ConsumerEvent);//让消费者赶紧运行消费
			}
			else//如果缓冲区不满  则设置2者均可运行
			{
				SetEvent(g_ProducerEvent);
				SetEvent(g_ConsumerEvent);
			}
			if(HasConsumerd>= ProNum)//如果已经消费的数量达到了要求的总产品数量 就不再消费 别忘记释放临界区!
			{
				LeaveCriticalSection(&g_cs);
				break;
			}
			printf("消费者%d消费了一个产品  编号为%d 它存放在坐标%d 还剩产品%d个\n", k, g_pBuffer[g_BufferOff], g_BufferOff, count - 1);
			count--;
			HasConsumerd++;
			g_BufferOff = (g_BufferOff - 1 + 10) % vol;
			Sleep(100);
		}
		else//当前无产品
		{
			ResetEvent(g_ConsumerEvent);
			SetEvent(g_ProducerEvent);
			Sleep(100);
		}
		LeaveCriticalSection(&g_cs);
	}
	return 0;
}
int main()
{
	HANDLE Producer1, Producer2, Producer3, Consumer, Consumer2;
    g_BufferOff = -1;
	count = 0;
	g_pBuffer = new int [vol];
	int k1 = 1, k2 = 2, k3 = 3, t1 = 1, t2 = 2;

	//初始化临界区 创建事件  创建线程
	InitializeCriticalSection(&g_cs);
	g_ProducerEvent = CreateEvent(NULL, TRUE, TRUE, "Producer");
	g_ConsumerEvent = CreateEvent(NULL, TRUE, FALSE, "Consumer");
	Producer1 = (HANDLE)_beginthreadex(NULL, 0, FunPro, &k1, 0, NULL);
	Producer2 = (HANDLE)_beginthreadex(NULL, 0, FunPro, &k2, 0, NULL);
	Producer3 = (HANDLE)_beginthreadex(NULL, 0, FunPro, &k3, 0, NULL);
	Consumer = (HANDLE)_beginthreadex(NULL, 0, FunCon, &t1, 0, NULL);
	Consumer2 = (HANDLE)_beginthreadex(NULL, 0, FunCon, &t2, 0, NULL);
	Sleep(15000000);

	//以下全是销毁工作
	CloseHandle(g_ConsumerEvent);
	CloseHandle(g_ProducerEvent);
	CloseHandle(Producer1);
	CloseHandle(Producer2);
	CloseHandle(Producer3);
	CloseHandle(Consumer);
	CloseHandle(Consumer2);
    delete []g_pBuffer;
	DeleteCriticalSection(&g_cs);
	system("pause");
	return 0;
}

法二:

#include <stdio.h>
#include <Windows.h>
#include <process.h>
int g_vol = 10;//缓冲区容量
int *g_pBuffer, g_BufferOff = -1, count;//依次为缓冲区,指向当前产品存放的位置, 目前缓冲区中产品数量
HANDLE g_hSemaphoreFull, g_hSemaphoreEmpty;//缓冲区已用位置信号量 未用位置信号量句柄
CRITICAL_SECTION g_cs;//临界区
int g_ProNum = 20;//要生产的产品数量
int g_HasProduced = 0;//已经生产了的产品数量
int g_HasConsumerd = 0;
unsigned int __stdcall FunPro(PVOID Pv)//生产者
{
	int k = *(int *)Pv;//生产者编号
    while(1)
	{
            WaitForSingleObject(g_hSemaphoreEmpty, INFINITE);
			EnterCriticalSection(&g_cs);
			if(g_HasProduced >= g_ProNum) break;
            g_HasProduced++;//已经生产国多少个产品了 包括已经消费的  
            g_BufferOff = (g_BufferOff + 1) % g_vol;//当前生产的产品要存储的位置  
            g_pBuffer[g_BufferOff] = g_HasProduced;  
            printf("生产者 %d 生产出一个产品,编号为 %d 存储位置%d \n",k, g_HasProduced, g_BufferOff); 
            ReleaseSemaphore(g_hSemaphoreFull, 1, NULL);//将Full信号量+1   表示已经有了一个产品
			LeaveCriticalSection(&g_cs);
			Sleep(10); 
			
	}
	return 0;
}

unsigned int __stdcall FunCon(PVOID Pv)
{
	int k = *(int *)Pv;//消费者编号
    while(1)
	{
			WaitForSingleObject(g_hSemaphoreFull, INFINITE);
			EnterCriticalSection(&g_cs);
			if(g_HasConsumerd >= g_ProNum) break;
			printf("消费者%d消费了一个产品  编号为%d 它存放在坐标%d \n", k, g_pBuffer[g_BufferOff], g_BufferOff);  
            count--;  
            g_HasConsumerd++;
            g_BufferOff = (g_BufferOff - 1 + 10) % g_vol;  
			ReleaseSemaphore(g_hSemaphoreEmpty, 1, NULL);
		    LeaveCriticalSection(&g_cs);
            Sleep(100);  
	}
	return 0;
}
int main()
{
	HANDLE Producer1, Producer2, Producer3, Consumer, Consumer2;
    g_BufferOff = -1;
	count = 0;
	g_pBuffer = new int [g_vol];
	int k1 = 1, k2 = 2, k3 = 3, t1 = 1, t2 = 2;

	//初始化临界区 创建事件  创建线程
	InitializeCriticalSection(&g_cs);
    g_hSemaphoreFull = CreateSemaphore(NULL, 0 , 100, "Full");//创建信号量 初始资源数量为0  最大并发数量我设置的100 
	g_hSemaphoreEmpty = CreateSemaphore(NULL, g_vol, 100, "Empty");
	Producer1 = (HANDLE)_beginthreadex(NULL, 0, FunPro, &k1, 0, NULL);
	Producer2 = (HANDLE)_beginthreadex(NULL, 0, FunPro, &k2, 0, NULL);
	Producer3 = (HANDLE)_beginthreadex(NULL, 0, FunPro, &k3, 0, NULL);
	Consumer = (HANDLE)_beginthreadex(NULL, 0, FunCon, &t1, 0, NULL);
	Consumer2 = (HANDLE)_beginthreadex(NULL, 0, FunCon, &t2, 0, NULL);
	Sleep(15000000);

	//以下全是销毁工作
	CloseHandle(g_hSemaphoreFull);
	CloseHandle(g_hSemaphoreEmpty);
	CloseHandle(Producer1);
	CloseHandle(Producer2);
	CloseHandle(Producer3);
	CloseHandle(Consumer);
	CloseHandle(Consumer2);
    delete []g_pBuffer;
	DeleteCriticalSection(&g_cs);
	system("pause");
	return 0;
}