原文来自秒杀多线程 第十章内容。链接:
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、生产不生产了,但是消费者没收到通知,一直等待,程序效率较低,如何解决?
是不是有一种线程间的异步通信机制
说一点体会,在本题编写过程中再次感受到代码设计的重要性,在进行代码编写之前,需要考虑好程序架构、流程和模型,还有临界值,当思路清晰之后再写代码能减少盲敲的回撤修改的痛苦感。

1302

被折叠的 条评论
为什么被折叠?



