关闭

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

2535人阅读 评论(0) 收藏 举报
 
多线程同步中的生产者消费者问题
 
生产者消费者问题是多线程同步中的一个经典问题,类似的还有读者写者问题和哲学家就餐问题。本文讨论生产者消费者问题,并分别在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);
}

 
 
 
 
 
 
 
 
 
 
0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:23539次
    • 积分:419
    • 等级:
    • 排名:千里之外
    • 原创:17篇
    • 转载:0篇
    • 译文:0篇
    • 评论:1条