生产者消费者模型



原文来自秒杀多线程 第十章内容。链接: http://blog.youkuaiyun.com/morewindows/article/details/7577591

主要考察多生产者,多消费者,多缓存的情况。

最开始由单生产者、单消费者单缓存开始,此时因为只有一个缓存,所以生产者和消费者对缓存的访问是互斥的。采用互斥访问。
当缓存大于1个时,生产者和消费者就可以同时访问缓存了。当生产者或者消费者大于1时,生产者之间也是互斥的,消费者之间是互斥的。
而当缓存是多缓存时,采用信号量的方式最为合适,信号量可以计数,而mutex则类似开关。

因此针对 多生产者,多消费者,多缓存情况,问题分析:
1、生产者需要缓存中有空位的时候才能往其中放,用一个信号量 SemaphoreEmpty 表示
2、消费者需要缓存中有数据的时候才往其中放,用一个信号量 SemaphoreFull 表示
3、生产者之间放缓存是互斥的,用一个临界区 CSProducer 表示
4、消费者之间取缓存是互斥的,用一个临界区 CSConsumer 表示

生产者线程:
P(SemaphoreEmpty)
P(CSProducer)
放数据
V(CSProducer)
V(SemaphoreFull)

消费者线程:
P(SemaphoreFull)
P(CSConsumer)
取数据
V(CSConsumer)
V(SemaphoreEmpty)

以上即为该程序的核心思想。在实际编程时,需要多考虑了以下问题:
1、生产者线程产生了足够的数据后,不再生产了,如何通知其他生产者线程 不要再生产了?
    采用一个全局变量开关,置于生产者互斥访问的代码内部,进入互斥代码内部先检验是否还要生产,是则继续,否则退出互斥代码段。
2、消费这线程在取完最后一个数据后,如何通知其他消费者线程不要再来取了?
    同样采用一个全局变量开关,置于消费者互斥访问的代码内部。需要注意的另外一点,由于其他生产者仍在等待信号量,而无法进入判断语句。因此最后一个消费者在取出数据后,要进行  剩余消费者线程数*V(semaphoreFull) 次操作。这样其他消费者进程才能继续执行以便退出。
3、多缓存怎么表示?
    根据源博客,环形数组是一个很好用的结构
4、在我们的实例程序中,由于要采用标准输出演示,因此需要加一个对 输出设备互斥访问的 互斥量。

顺便考虑一个问题:
5、生产不生产了,但是消费者没收到通知,一直等待,程序效率较低,如何解决?
    是不是有一种线程间的异步通信机制
说一点体会,在本题编写过程中再次感受到代码设计的重要性,在进行代码编写之前,需要考虑好程序架构、流程和模型,还有临界值,当思路清晰之后再写代码能减少盲敲的回撤修改的痛苦感。

代码如下:
// 生产者消费者.cpp : 定义控制台应用程序的入口点。
//单生产者,单消费者,多缓冲区
//采用信号量方式,环形缓冲区
//2014-8-21 20:54:50 by www

#include "stdafx.h"
#include <process.h>
#include <Windows.h>
#include <iostream>
using namespace std;

//线程同步互斥相关
CRITICAL_SECTION g_csProduceCode, g_csCosumeCode;
HANDLE g_SemaphoreEmpty, g_SemaphoreFull ;
HANDLE g_ThreadEvent;
HANDLE g_hMutex;

//相关常量
const int Source_Num = 50;
const int Buffers_Size= 8;
const int Thread_Mid=4;
const int Thread_Num = 8;
volatile int g_SourceIndex = 0;
volatile bool g_ConsumerOver = false;
volatile bool g_ProducerOver = false;

volatile int g_in=0 ,g_out=0, g_buffer[Buffers_Size]={-1};

BOOL SetConsoleColor(WORD wAttributes)
{
	HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
	if( hConsole == INVALID_HANDLE_VALUE)
		return FALSE;
	return SetConsoleTextAttribute(hConsole, wAttributes);
}

unsigned int __stdcall ProduceThread(void *pPM)
{
	int nThreadNum = *(int*)pPM;
	SetEvent(g_ThreadEvent);
	volatile bool flag = true;

	while( flag )
	{
		WaitForSingleObject(g_SemaphoreEmpty, INFINITE);

		EnterCriticalSection(&g_csProduceCode);
		if( g_ProducerOver )
		{
			LeaveCriticalSection(&g_csProduceCode);
			break;
		}
		g_buffer[g_in%Buffers_Size] = g_SourceIndex;
		g_in= (g_in+1)%Buffers_Size;
		WaitForSingleObject(g_hMutex, INFINITE);
		wcout<<_T("生产者")<<nThreadNum<<_T("将资源")<<g_SourceIndex<<_T("放入仓库")<<endl;
		ReleaseMutex(g_hMutex);

		g_SourceIndex++;
		if( g_SourceIndex == Source_Num )
		{
			g_ProducerOver = true;
			LeaveCriticalSection(&g_csProduceCode);
			ReleaseSemaphore(g_SemaphoreFull,1,NULL);
			break;
		}

		LeaveCriticalSection(&g_csProduceCode);

		ReleaseSemaphore(g_SemaphoreFull, 1, NULL);
	}
	WaitForSingleObject(g_hMutex, INFINITE);
	wcout<<nThreadNum<<_T("生产者退出")<<endl;
	ReleaseMutex(g_hMutex);
	return 0;
}


unsigned int __stdcall ConsumeThread(void *pPM)
{
	volatile bool flag = true;
	int nThreadNum = *(int*)pPM;
	SetEvent(g_ThreadEvent);

	while(flag)
	{
		WaitForSingleObject(g_SemaphoreFull, INFINITE);

		EnterCriticalSection(&g_csCosumeCode);
		if( g_ConsumerOver )
		{
			LeaveCriticalSection(&g_csCosumeCode);
			break;
		}
		WaitForSingleObject(g_hMutex, INFINITE);
		SetConsoleColor(FOREGROUND_RED);
		wcout<<_T("	消费者")<<nThreadNum<<_T("将资源")<<g_buffer[g_out]<<_T("从仓库取出")<<endl;
		SetConsoleColor(FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_GREEN);
		ReleaseMutex(g_hMutex);

		if( g_buffer[g_out] == Source_Num-1)
		{
			g_ConsumerOver = true;
			LeaveCriticalSection(&g_csCosumeCode);
			//通知其他消费者没有库存了
			for( int index = Thread_Mid+1 ; index <Thread_Num; index++)
				ReleaseSemaphore(g_SemaphoreFull,1,NULL);
			break;
		}
		g_out = (g_out+1)%Buffers_Size;
		LeaveCriticalSection(&g_csCosumeCode);

		ReleaseSemaphore(g_SemaphoreEmpty, 1, NULL);
	}
	WaitForSingleObject(g_hMutex, INFINITE);
	SetConsoleColor(FOREGROUND_RED);
	wcout<<nThreadNum<<_T("消费者退出")<<endl;
	SetConsoleColor(FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_GREEN);
	ReleaseMutex(g_hMutex);
	return 0;
}


int _tmain(int argc, _TCHAR* argv[])
{
	wcout.imbue(locale(locale(), "", LC_CTYPE));

	InitializeCriticalSection(&g_csProduceCode);
	InitializeCriticalSection(&g_csCosumeCode);
	g_SemaphoreEmpty= CreateSemaphore(NULL, Buffers_Size, Buffers_Size, NULL);
	g_SemaphoreFull = CreateSemaphore(NULL, 0, Buffers_Size, NULL);
	g_ThreadEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
	g_hMutex = CreateMutex(NULL, FALSE, NULL);

	HANDLE handle[Thread_Num];
	for (int i=0 ; i< Thread_Mid ;i++)
	{
		handle[i]= (HANDLE)_beginthreadex(NULL, 0, ProduceThread, &i,0, NULL);
		WaitForSingleObject(g_ThreadEvent, INFINITE);
	}
	for(int i=Thread_Mid ; i< Thread_Num ; i++)
	{
		handle[i]= (HANDLE)_beginthreadex(NULL, 0, ConsumeThread, &i, 0, NULL);
		WaitForSingleObject(g_ThreadEvent, INFINITE);
	}

	WaitForMultipleObjects(Thread_Num, handle, TRUE, INFINITE);
	for ( int index = 0; index < Thread_Num; index++)
		CloseHandle( handle[index]);

	DeleteCriticalSection(&g_csProduceCode);
	DeleteCriticalSection(&g_csCosumeCode);
	CloseHandle(g_SemaphoreFull);
	CloseHandle(g_SemaphoreEmpty);
	system("pause");
	return 0;
}
当消费者、生产者均为4时,缓存大小为8时
程序结果:





原文来自秒杀多线程 第十章内容。链接: http://blog.youkuaiyun.com/morewindows/article/details/7577591

主要考察多生产者,多消费者,多缓存的情况。

最开始由单生产者、单消费者单缓存开始,此时因为只有一个缓存,所以生产者和消费者对缓存的访问是互斥的。采用互斥访问。
当缓存大于1个时,生产者和消费者就可以同时访问缓存了。当生产者或者消费者大于1时,生产者之间也是互斥的,消费者之间是互斥的。
而当缓存是多缓存时,采用信号量的方式最为合适,信号量可以计数,而mutex则类似开关。

因此针对 多生产者,多消费者,多缓存情况,问题分析:
1、生产者需要缓存中有空位的时候才能往其中放,用一个信号量 SemaphoreEmpty 表示
2、消费者需要缓存中有数据的时候才往其中放,用一个信号量 SemaphoreFull 表示
3、生产者之间放缓存是互斥的,用一个临界区 CSProducer 表示
4、消费者之间取缓存是互斥的,用一个临界区 CSConsumer 表示

生产者线程:
P(SemaphoreEmpty)
P(CSProducer)
放数据
V(CSProducer)
V(SemaphoreFull)

消费者线程:
P(SemaphoreFull)
P(CSConsumer)
取数据
V(CSConsumer)
V(SemaphoreEmpty)

以上即为该程序的核心思想。在实际编程时,需要多考虑了以下问题:
1、生产者线程产生了足够的数据后,不再生产了,如何通知其他生产者线程 不要再生产了?
    采用一个全局变量开关,置于生产者互斥访问的代码内部,进入互斥代码内部先检验是否还要生产,是则继续,否则退出互斥代码段。
2、消费这线程在取完最后一个数据后,如何通知其他消费者线程不要再来取了?
    同样采用一个全局变量开关,置于消费者互斥访问的代码内部。需要注意的另外一点,由于其他生产者仍在等待信号量,而无法进入判断语句。因此最后一个消费者在取出数据后,要进行  剩余消费者线程数*V(semaphoreFull) 次操作。这样其他消费者进程才能继续执行以便退出。
3、多缓存怎么表示?
    根据源博客,环形数组是一个很好用的结构
4、在我们的实例程序中,由于要采用标准输出演示,因此需要加一个对 输出设备互斥访问的 互斥量。

顺便考虑一个问题:
5、生产不生产了,但是消费者没收到通知,一直等待,程序效率较低,如何解决?
    是不是有一种线程间的异步通信机制
说一点体会,在本题编写过程中再次感受到代码设计的重要性,在进行代码编写之前,需要考虑好程序架构、流程和模型,还有临界值,当思路清晰之后再写代码能减少盲敲的回撤修改的痛苦感。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值