学习MoreWindows的《秒杀多线程系列》至第十篇生产者消费者问题,对于多线程也可以说是有了一知半解了,特作一篇博文进行一点总结。在此,非常感谢MoreWindows为我们大家提供这么好的学习资料。
生产者消费者问题描述:有一个生产者在生产产品,这些产品将提供给若干个消费者去消费,为了使生产者和消费者能并发执行,在两者之间设置一个具有多个缓冲区的缓冲池,生产者将它生产的产品放入一个缓冲区中,消费者可以从缓冲区中取走产品进行消费,显然生产者和消费者之间必须保持同步,即不允许消费者到一个空的缓冲区中取产品,也不允许生产者向一个已经放入产品的缓冲区中再次投放产品,同时在本文中也保证消费者和生产者对缓冲区的操作是互斥的(其实是没必要的)。
在本文中,利用一个互斥量来保证保证消费者和生产者对缓冲区的操作是互斥的,用proOK和conOK信号量来保证生产者和消费者之间的同步。
第一个版本的程序:
#include <stdio.h>
#include <process.h>
#include <windows.h>
const int produceNum = 8;
const int bufferSize = 4;
char buffer[bufferSize];
CRITICAL_SECTION cs1;
HANDLE proOK,conOK;
int g_i,g_j;
unsigned int __stdcall ProFun(PVOID pPM)
{
for(int i=1;i<=produceNum;i++)
{
WaitForSingleObject(proOK,INFINITE);
EnterCriticalSection(&cs1);
buffer[g_i]=i;
printf("生产者在第%d个缓冲区中投入了数据%d\n",g_i,i);
g_i = (g_i+1)%bufferSize;
LeaveCriticalSection(&cs1);
ReleaseSemaphore(conOK,1,NULL);
}
printf(" 生产者完成任务,线程结束运行\n");
return 0;
}
unsigned int __stdcall ConFun(PVOID pPM)
{
while(TRUE)
{
WaitForSingleObject(conOK,INFINITE);
EnterCriticalSection(&cs1);
printf(" 编号为%d的消费者从第%d个缓冲区取走数据%d\n",(int)GetCurrentThreadId(),g_j,buffer[g_j]);
if(buffer[g_j] == produceNum)
{
LeaveCriticalSection(&cs1);
ReleaseSemaphore(conOK,1,NULL);//通知另一个消费者也退出
break;
}
g_j=(g_j+1)%bufferSize;
LeaveCriticalSection(&cs1);
Sleep(50);
ReleaseSemaphore(proOK,1,NULL);
}
printf(" 编号为%d的线程收到结束信号,退出。。。。。\n",(int)GetCurrentThreadId());
return 0;
}
int main()
{
InitializeCriticalSection(&cs1);
proOK=CreateSemaphore(NULL,4,4,NULL);
conOK=CreateSemaphore(NULL,0,4,NULL);
g_i=0;
g_j=0;
const int THREAD_NUM =3;
HANDLE handle[THREAD_NUM];
handle[0]=(HANDLE)_beginthreadex(NULL,0,ProFun,NULL,0,NULL);
handle[1]=(HANDLE)_beginthreadex(NULL,0,ConFun,NULL,0,NULL);
handle[2]=(HANDLE)_beginthreadex(NULL,0,ConFun,NULL,0,NULL);
WaitForMultipleObjects(THREAD_NUM, handle, TRUE, INFINITE);
for(int i=0;i<3;i++)
CloseHandle(handle[i]);
CloseHandle(proOK);
CloseHandle(conOK);
DeleteCriticalSection(&cs1);
return 0;
}
运行结果截图:
从截图可以看到:产品8被两个消费者取了两次,原因在哪里呢:
if(buffer[g_j] == produceNum)
{
LeaveCriticalSection(&cs1);
ReleaseSemaphore(conOK,1,NULL);
//isover = TRUE;
break;
}
这段程序的作用是:当某一个消费者取到最后一个产品时,就退出,但为了确保另外一个消费者也能够正常退出,所以通过这句话ReleaseSemaphore(conOK,1,NULL);通知另外一个程序也及时退出,这样就造成了第二个消费者本来不能消费了的,但是又可以消费一次了(conOK信号量+1),这样两个消费者就都取了一次产品8.好吧,那这样的话,最后,如果某一个线程取到最后一个产品后就退出,而不管另一个线程是否会退出,即去掉这句ReleaseSemaphore(conOK,1,NULL);另外一个线程是否会正常退出呢。来看一下运行结果:
根据截图,我们看到,程序阻塞在第二个线程退出之前了,这是因为在第一个消费者退出后,conOK信号量已为0,然而第二个消费者已经开始准备去消费了,但又得不到消费的允许,就只有一直在等了。
那我们怎样才能让两个消费者线程对产品8只消费一次的同时,又能够保证两个线程都可以同时退出呢。在第一个程序中,两个线程是无条件的就可以准备消费(while(TRUE)),所以就出现了那种已经准备消费了,但是又得不到消费的允许,进一步就无法获得消费结束的信号,所以程序一直在等待。这样,我们就想到是不是可以给消费者设置一个可以准备消费的条件,所以,就加一个bool变量就可以了,当这个变量为真时,可以准备进行消费,否则准备消费都不允许,修改后的程序如下:
#include <stdio.h>
#include <process.h>
#include <windows.h>
const int produceNum = 8;
const int bufferSize = 4;
char buffer[bufferSize];
CRITICAL_SECTION cs1;
HANDLE proOK,conOK;//生产者消费者允许生产和消费的信号
int g_i,g_j;
bool isover;
unsigned int __stdcall ProFun(PVOID pPM)
{
for(int i=1;i<=produceNum;i++)
{
WaitForSingleObject(proOK,INFINITE);
EnterCriticalSection(&cs1);
buffer[g_i]=i;
printf("生产者在第%d个缓冲区中投入了数据%d\n",g_i,i);
g_i = (g_i+1)%bufferSize;
LeaveCriticalSection(&cs1);
ReleaseSemaphore(conOK,1,NULL);
}
printf(" 生产者完成任务,线程结束运行\n");
return 0;
}
unsigned int __stdcall ConFun(PVOID pPM)
{
while(!isover)
{
WaitForSingleObject(conOK,INFINITE);
EnterCriticalSection(&cs1);
printf(" 编号为%d的消费者从第%d个缓冲区取走数据%d\n",(int)GetCurrentThreadId(),g_j,buffer[g_j]);
if(buffer[g_j] == produceNum)
{
LeaveCriticalSection(&cs1);
//ReleaseSemaphore(conOK,1,NULL);
isover = TRUE;
break;
}
g_j=(g_j+1)%bufferSize;
LeaveCriticalSection(&cs1);
Sleep(50);
ReleaseSemaphore(proOK,1,NULL);
}
printf(" 编号为%d的线程收到结束信号,退出。。。。。\n",(int)GetCurrentThreadId());
return 0;
}
int main()
{
InitializeCriticalSection(&cs1);
proOK=CreateSemaphore(NULL,4,4,NULL);
conOK=CreateSemaphore(NULL,0,4,NULL);
g_i=0;
g_j=0;
isover = FALSE;
const int THREAD_NUM =3;
HANDLE handle[THREAD_NUM];
handle[0]=(HANDLE)_beginthreadex(NULL,0,ProFun,NULL,0,NULL);
handle[1]=(HANDLE)_beginthreadex(NULL,0,ConFun,NULL,0,NULL);
handle[2]=(HANDLE)_beginthreadex(NULL,0,ConFun,NULL,0,NULL);
WaitForMultipleObjects(THREAD_NUM, handle, TRUE, INFINITE);
for(int i=0;i<3;i++)
CloseHandle(handle[i]);
CloseHandle(proOK);
CloseHandle(conOK);
DeleteCriticalSection(&cs1);
return 0;
}
运行结果:
从结果看到,两个消费者对8号产品消费了一次,并且都正常的退出了线程。
最后,再一次感谢MoreWinsows给我们提供了这么好的学习资料。
分享到:
相关推荐
用进程同步方法解决“生产者-消费者”问题,C或C++语言实现。 1、设计目的 通过研究进程并发和信号量机制,实现生产者-消费者问题的并发控制。 2、设计要求 1)每个生产者和消费者对有界缓冲区进行操作后,即时显示...
利用记录型信号量解决生产者-消费者问题.doc
这是一个操作系统的课程设计 ,关于在linux下用多进程同步方法解决生产者-消费者问题。 有论文和源码
用多线程同步方法解决生产者-消费者问题(操作系统课设
以记录型信号量实现生产者-消费者问题 实验目的: 1.加深对进程同步概念的理解。 2.理解多道程序环境中,不同进程对资源访问及相互合作进程的关系的处理方法。 实验要求: 利用C语言程序模拟生产者-消费者问题和哲学...
这是一个在linux系统下用多进程同步的方法解决消费者-生产者问题的源代码,是关于操作系统的。
1、设计目的:通过研究Linux的进程机制和信号量,实现生产者消费者问题的并发控制。 2、说明:有界缓冲区内设有20个存储单元,放入取出的产品设定为1-20个整数。 3、设计要求: 生产者和消费者进程的数目不固定,可...
用多线程同步方法解决生产者-消费者问题(操作系统课设)
在 Ubuntu 下编写程序,用信号量解决生产者-消费者问题 在 Ubuntu 下编写应用程序 pc.c,解决经典的生产者-消费者问题,完成下面的功能: 建立一个生产者进程,N 个消费者进程(N > 1) 用文件建立一个共享缓冲区 生产...
1.利用记录型信号量解决生产者-消费者问题.odt1.利用记录型信号量解决生产者-消费者问题.odt1.利用记录型信号量解决生产者-消费者问题.odt
并发控制-生产者-消费者问题实验报告
1.每个生产者和消费者对有界缓冲区进行操作后,即时显示有界缓冲区的全部内容、当前指针位置和生产者/消费者线程的标识符; 2.生产者和消费者各有两个以上; 3.多个生产者或多个消费者之间须共享对缓冲区进行...
生产者-消费者问题的源代码(操作系统),可以正确运行。
生产者——消费者问题实际上是相互合作进程关系的一种抽象。该类问题不允许消费者进程到一个空缓冲区中取产品,同时也不允许生产者进程到一个已满且还没被取走的缓冲区中投放产品。 使用一个数组来表示具有n个(0,1...
并发控制-生产者-消费者问题
计算机课程设计 操作系统生产者-消费者问题
生产者消费者问题,C++。生产者-消费者(producer-consumer)问题,也称作有界缓冲区(bounded-buffer)问题,两个进程共享一个公共的固定大小的缓冲区。其中一个是生产者,用于将消息放入缓冲区;另外一个是消费者...
生产者-消费者问题描述的是:有一群生产者进程在生产产品,并将这些产品提供给消费者进程去消费。为使生产者进程与消费者进程能够并发执行,在两者之间设置了一个具有n个缓冲区的缓冲池,生产者进程将它所生产的产品...
设计要求:(1)每个生产者和消费者对有界缓冲区进行操作后,即时显示有界缓冲区的全部内容,当前指针位置和生产者/消费者线程的标识符.(2)生产者和消费者各有两个以上.(3)多个生产者或多个消费者之间须有共享对缓冲区...