多线程同步中的生产者消费者问题 - windows 平台实现

 
多线程同步中的生产者消费者问题
 
生产者消费者问题是多线程同步中的一个经典问题,类似的还有读者写者问题和哲学家就餐问题。本文讨论生产者消费者问题,并分别在 windows linux 平台上进行了程序实现。
生产者消费者问题的基本概念是:一个或多个生产者向一个缓冲区添加数据 ( 或消息 ) ,一个或多个消费者使用这些数据完成特定的功能。每个生产者是一个独立线程,每个消费者也是一个线程。例如, 2 个线程从网络 socket 接收数据,放到一个缓冲区,另外 3 个线程负责处理这些数据。生产者消费者问题主要应该解决线程之间的互斥和同步问题。
生产者消费者问题与读者写者问题有些不同,因为读者写者问题中,读者不修改任何共享变量,而生产者消费者问题中消费者也需要修改某些共享变量。另外,生产者消费者问题中还要考虑缓冲区的满和空的问题。根据生产者和消费者是否修改同一个共享变量,可以分成两种情况:
(1)    生产者和消费者需要修改同一个共享变量:
如果共享缓冲区是一个类似于栈的东西,如下图所示:
    
 
其中 P C 分别表示生产者和消费者当前位置指示器,二者共享栈顶指针。我们用一个互斥量保护共享的栈顶指针变量,该互斥量不仅用于防止生产者和消费者对共享变量的修改,而且用于生产者之间以及消费者之间的互斥访问。我们用两个信号量分别指示栈的空和满,以协调生产者和消费者之间的操作。
(2)    生产者和消费者不需要修改共享变量:
如果共享缓冲区是一个类似于循环队列的东西,如下图所示:
   
 
生产者和消费者当前位置指示器 P C 不共享变量,各自维护自己的指针。不需要一个互斥量来防止生产者和消费者对共享变量的修改,但是需要两个互斥量分别用于生产者之间和消费者之间的互斥。同样需要两个信号量分别指示队列的空和满。
我们只对第二种情况进行程序实现,因为第一种情况相当于第二种情况的一个特殊情况。
 

(1) ProducerConsumerLock.h

#ifndef ProducerConsumerLock_H
#define ProducerConsumerLock_H
class ProducerConsumerLock
{
protected:
 HANDLE produceMutex;
 HANDLE consumeMutex;
 HANDLE wakenProducerSemaph;
 HANDLE wakenConsumerSemaph;
 unsigned int bufferLen;
public:
 ProducerConsumerLock(unsigned int bufLen=100)
 {
  bufferLen = bufLen;
  // create 2 Mutex and 2 Semaphore
  produceMutex = CreateMutex(NULL, false, NULL);
  consumeMutex = CreateMutex(NULL, false, NULL);
  wakenProducerSemaph = CreateSemaphore(NULL, bufferLen, bufferLen, NULL);
  wakenConsumerSemaph = CreateSemaphore(NULL, 0, bufferLen, NULL);
  if (produceMutex == NULL || consumeMutex == NULL ||
   wakenProducerSemaph == NULL || wakenConsumerSemaph == NULL)
  {
   MessageBox(NULL, "create LOCK failed!" , NULL, 0);
  }
 }
 ~ProducerConsumerLock()
 {
  CloseHandle(produceMutex);
  CloseHandle(consumeMutex);
  CloseHandle(wakenProducerSemaph);
  CloseHandle(wakenConsumerSemaph);
 }
 inline void ProducerLock()
 {
  // if the buffer is full, then wait
  WaitForSingleObject(wakenProducerSemaph, INFINITE);
  // if other producer is producing, then wait
  WaitForSingleObject(produceMutex, INFINITE);
 }
 inline void ProducerUnLock()
 {
  // notify other producer
  ReleaseMutex(produceMutex);
  // waken consumers
  ReleaseSemaphore(wakenConsumerSemaph, 1, NULL);
 }
 inline void ConsumerLock()
 {
  // if the buffer is empty, then wait
  WaitForSingleObject(wakenConsumerSemaph, INFINITE);
  // if other consumer is consuming, then wait
  WaitForSingleObject(consumeMutex, INFINITE);
 }
 inline void ConsumerUnLock()
 {
  // notify other consumer
  ReleaseMutex(consumeMutex);
  // waken producers
  ReleaseSemaphore(wakenProducerSemaph, 1, NULL);
 }
};

#endif
 
(2) ProducerConsumer.cpp
// ProducerConsumer.cpp : Defines the entry point for the application.
//
#include "stdafx.h"
#include
#include
#include
#include
#include
#include
#include "ProducerConsumerLock.h"
char sharedQueue[128]={0};
const unsigned int queueLen = 10;
unsigned int producerPointer=0;
unsigned int consumerPointer=0;
char logFile[128]="d://log.txt";
ProducerConsumerLock* pcLock=new ProducerConsumerLock(10);
void producerProc(void* param);
void consumerProc(void* param);
void WriteLogStr(char* s);
using namespace std;
int APIENTRY WinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR     lpCmdLine,
                     int       nCmdShow)
{
  // TODO: Place code here.
 int producerId;
 int consumerId;
 FILE* f;
 // clear the log
 f = fopen(logFile, "w");
 fclose(f);
 srand(time(0));
 producerId = 1;
 _beginthread(producerProc, 0, (void*)&producerId);
 Sleep(10);
 consumerId = 1;
 _beginthread(consumerProc, 0, (void*)&consumerId);
 Sleep(10);
 producerId = 2;
 _beginthread(producerProc, 0, (void*)&producerId);
 Sleep(10);
 consumerId = 2;
 _beginthread(consumerProc, 0, (void*)&consumerId);
 Sleep(10);
 producerId = 3;
 _beginthread(producerProc, 0, (void*)&producerId);
 // running for 5s
 Sleep(5000);
 return 0;
}
void producerProc(void* param)
{
 int myid;
 char idStr[128];
 //char tmpStr[128];
 char str[128];
 myid = *((int*)(param));
 itoa(myid, idStr, 10);
 strcpy(str, "producer ");
 strncat(str, idStr, 128);
 strcat(str, " begin......");
 //cout << "reader " << myid << " begin......" << endl;
 WriteLogStr(str);
 while (true)
 {
  // first sleep a random time : between 1 - 5 s
  int sleepTime;
  sleepTime = 1 + (int)(5.0*rand()/(RAND_MAX+1.0));
  Sleep(sleepTime*10);
  // get a random char
  int randChar;
  randChar = myid + (int)(5.0*rand()/(RAND_MAX+1.0));
  randChar += 40;
  // prepare str
  strcpy(str, "producer ");
  strncat(str, idStr, 128);
  strcat(str, " has produced a new char :  ");
  int len = strlen(str);
  str[len] = randChar;
  str[len+1] = 0;
  // then access the shared var
  pcLock->ProducerLock();
   sharedQueue[producerPointer] = randChar;
   producerPointer = (producerPointer + 1) % queueLen;
   if (producerPointer == consumerPointer)
   {
    WriteLogStr("The buffer queue is full !!!");
   }
   //itoa(producerPointer, tmpStr, 10);
   //strncat(str, tmpStr, 128);
   //cout << "reader " << myid << " is reading the shared string : " << sharedStr << endl;
   WriteLogStr(str);
  pcLock->ProducerUnLock();

 }
}
void consumerProc(void* param)
{
 int myid;
 char idStr[128];
 //char tmpStr[128];
 char str[128];
 myid = *((int*)(param));
 itoa(myid, idStr, 10);
 strcpy(str, "consumer ");
 strncat(str, idStr, 128);
 strcat(str, " begin......");
 //cout << "reader " << myid << " begin......" << endl;
 WriteLogStr(str);
 while (true)
 {
  // first sleep a random time : between 1 - 5 s
  int sleepTime;
  sleepTime = 1 + (int)(5.0*rand()/(RAND_MAX+1.0));
  Sleep(sleepTime*10);
  // prepare str to print
  strcpy(str, "consumer ");
  strncat(str, idStr, 128);
  strcat(str, " has consumed a char : ");
  // then access the shared queue
  pcLock->ConsumerLock();
   int len = strlen(str);
   str[len] = sharedQueue[consumerPointer];
   str[len+1] = 0;
   sharedQueue[consumerPointer] = 0;
   consumerPointer = (consumerPointer + 1) % queueLen;
   //itoa(consumerPointer, tmpStr, 10);
   //strncat(str, tmpStr, 128);
   //cout << "reader " << myid << " is reading the shared string : " << sharedStr << endl;
   WriteLogStr(str);
  pcLock->ConsumerUnLock();
 }
}
void WriteLogStr(char* s)
{
 FILE* f;
 f = fopen(logFile, "a");
 if (f != NULL)
 {
  fwrite(s, strlen(s), 1, f);
  fwrite("/n", 1, 1, f);
 }
 fclose(f);
}

 
 
 
 
 
 
 
 
 
 

你可能感兴趣的:(多线程,windows,null,random,semaphore,buffer)