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

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

 

 

(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 <process.h>
#include <time.h>
#include <stdlib.h>
#include <stdio.h>
#include <iostream>
#include <string>
#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);
}

 

 

posted @ 2009-05-31 00:51  coding_rabbit  阅读(876)  评论(0编辑  收藏  举报