前言
生产者-消费者问题,实际上主要是包含了两类线程,一种是生产者线程用于生产数据,另一种是消费者线程用于消费数据,为了解耦生产者和消费者的关系,通常会采用共享的数据区域,就像是一个仓库,生产者生产数据之后直接放置在共享数据区中,并不需要关心消费者的行为;而消费者只需要从共享数据区中去获取数据,就不再需要关心生产者的行为。但是,这个共享数据区域中应该具备这样的线程间并发协作的功能
一、代码实现
#include <string>
#include <iostream>
#include <cstdlib>
#include <string>
#include <ctime>
#include "TQueue.h"
#include "zthread/Thread.h"
#include "zthread/Mutex.h"
#include "zthread/Guard.h"
#include "zthread/Condition.h"
#include "zthread/ThreadedExecutor.h"
using namespace ZThread;
using namespace std;
class Car{
private:
int id;
bool engine, driveTrain, wheels;
public:
Car(int idn) : id(idn), engine(false), driveTrain(false), wheels(false){}
Car() : id(-1), engine(false), driveTrain(false), wheels(false){}
int getid() { return id; }
void addEngine() { engine = true; }
bool engineInstalled() { return engine; }
void addDriveTrain() { driveTrain = true; }
bool driveTrainInstalled(){ return driveTrain;}
void addWheels(){ wheels = true; }
bool wheelsInstalled(){ return wheels;}
friend ostream& operator<<(ostream& os, const Car& c){
return os << "Car" << c.id << " [ " << " engine: " << c.engine << "driveTrain: " << c.driveTrain
<< " wheels: " << c.wheels << " ] ";
}
};
typedef CountedPtr<TQueue<Car>> CarQueue;
class ChassisBuilder : public Runnable{
private:
CarQueue carQueue;
int counter;
public:
ChassisBuilder(CarQueue& cq) : carQueue(cq), counter(0){}
void run(){
try{
while(!Thread::interrupted()){
Thread::sleep(1000);
Car c(counter++);/*Make chassis*/
cout << c << endl;/*Insert into queue*/
carQueue->put(c);
}
}catch(Interrupted_Exception&) {}
cout << "ChassisBuilder off" << endl;
}
};
class Cradle{
private:
Car c; /*Holds current car being worked on*/
bool occupied;
Mutex workLock, readyLock;
Condition workCondition, readyCondition;
bool engineBotHired, wheelBotHired, driveTrainBotHired;
public:
Cradle() : workCondition(workLock), readyCondition(readyLock){
occupied = false;
engineBotHired = true;
wheelBotHired = true;
driveTrainBotHired = true;
}
void InsertCar(Car chassis){
c = chassis;
occupied = true;
}
Car getCar(){/*Can only extract car once*/
if(!occupied){
cerr << "No Car in Cradle for getCar()" << endl;
return Car();
}
occupied = false;
return c;
}
Car* operator->(){ return &c; }
void offerEngineBotServices(){/*Allow robots to offer services to this Cradle*/
Guard<Mutex> g(workLock);
while(engineBotHired)
workCondition.wait();
engineBotHired = true;
}
void offerWheelBotServices(){
Guard<Mutex> g(workLock);
while(wheelBotHired)
workCondition.wait();
wheelBotHired = true;
}
void offerDriveTrainBotServices(){
Guard<Mutex> g(workLock);
while(driveTrainBotHired)
workCondition.wait();
driveTrainBotHired = true;
}
void startWork(){/* Tell waiting robots that work is ready*/
Guard<Mutex> g(workLock);
engineBotHired = false;
wheelBotHired = false;
driveTrainBotHired = false;
workCondition.broadcast();
}
void taskFinished(){/*Each robot reports when their job is done*/
Guard<Mutex> g(readyLock);
readyCondition.signal();
}
void waitUntilWorkFinished(){
Guard<Mutex> g(readyLock);
while(!(c.engineInstalled() && c.driveTrainInstalled() && c.wheelsInstalled()))
readyCondition.wait();
}
};
typedef CountedPtr<Cradle> CradlePtr;
class Director : public Runnable{
private:
CarQueue chassisQueue, finishingQueue;
CradlePtr cradle;
public:
Director(CarQueue& cq, CarQueue& fq, CradlePtr cr) : chassisQueue(cq), finishingQueue(fq), cradle(cr){}
void run(){
try{
while(!Thread::interrupted()){
cradle->InsertCar(chassisQueue->get());/*Blocks until chassis is available*/
cradle->startWork();/*Notify robots car is ready for work*/
cradle->waitUntilWorkFinished();/*Wait until work completes*/
finishingQueue->put(cradle->getCar());/*Put car into queue for future work*/
}
}catch(Interrupted_Exception&) {}
cout << " Director off" << endl;
}
};
class EngineRobot : public Runnable{
private:
CradlePtr cradle;
public:
EngineRobot(CradlePtr cr) : cradle(cr){}
void run(){
try{
while(!Thread::interrupted()){
cradle->offerEngineBotServices();/*Blocks until job is offered/accepted*/
cout << "Installing engine" << endl;
(*cradle)->addEngine();
cradle->taskFinished();
}
}catch(Interrupted_Exception&){}
cout << "EngineRobot off" << endl;
}
};
class DriveTrainRobot : public Runnable{
private:
CradlePtr cradle;
public:
DriveTrainRobot(CradlePtr cr) : cradle(cr){}
void run(){
try{
while(!Thread::interrupted()){
cradle->offerDriveTrainBotServices();/*Blocks until job is offered/accepted*/
cout << "Installing DriveTrain" << endl;
(*cradle)->addDriveTrain();
cradle->taskFinished();
}
}catch(Interrupted_Exception&){}
cout << "DriveTrainRobot off" << endl;
}
};
class WheelRobot : public Runnable{
private:
CradlePtr cradle;
public:
WheelRobot(CradlePtr cr) : cradle(cr){}
void run(){
try{
while(!Thread::interrupted()){
cradle->offerWheelBotServices();/*Blocks until job is offered/accepted*/
cout << "Installing Wheel" << endl;
(*cradle)->addWheels();
cradle->taskFinished();
}
}catch(Interrupted_Exception&){}
cout << "WheelRobot off" << endl;
}
};
class Reporter : public Runnable{
private:
CarQueue carQueue;
public:
Reporter(CarQueue& cq) : carQueue(cq){}
void run(){
try{
while(!Thread::interrupted()){
cout << carQueue->get() << endl;
}
}catch(Interrupted_Exception&){}
cout << "Reporter off" << endl;
}
};
int main() {
cout << "Press <Enter> to quit" << endl;
try{
CarQueue chassisQueue(new TQueue<Car>), finishingQueue(new TQueue<Car>);
CradlePtr cradle(new Cradle);
ThreadedExecutor assemblyLine;
assemblyLine.execute(new EngineRobot(cradle));
assemblyLine.execute(new DriveTrainRobot(cradle));
assemblyLine.execute(new WheelRobot(cradle));
assemblyLine.execute(new Director(chassisQueue, finishingQueue, cradle));
assemblyLine.execute(new Reporter(finishingQueue));
assemblyLine.execute(new ChassisBuilder(chassisQueue));
cin.get();
assemblyLine.interrupt();
}catch(Synchronization_Exception& e){
cerr << e.what() << endl;
}
}
二、实验验证
ThreadQueue created
Press <Enter> to quit
User thread created.
Reference thread created.
1 reference-thread added.
pollPendingThreads()
1 user-thread added.
Thread starting...
User thread created.
pollPendingThreads()
1 user-thread added.
Thread starting...
User thread created.
pollPendingThreads()
1 user-thread added.
Thread starting...
User thread created.
pollPendingThreads()
1 user-thread added.
Thread starting...
User thread created.
pollPendingThreads()
1 user-thread added.
Thread starting...
User thread created.
pollPendingThreads()
1 user-thread added.
Thread starting...
Car0 [ engine: 0driveTrain: 0 wheels: 0 ]
Installing engine
Installing DriveTrain
Installing Wheel
Car0 [ engine: 1driveTrain: 1 wheels: 1 ]
Car1 [ engine: 0driveTrain: 0 wheels: 0 ]
Installing engine
Installing Wheel
Installing DriveTrain
Car1 [ engine: 1driveTrain: 1 wheels: 1 ]
Car2 [ engine: 0driveTrain: 0 wheels: 0 ]
Installing DriveTrain
Installing engine
Installing Wheel
Car2 [ engine: 1driveTrain: 1 wheels: 1 ]
Car3 [ engine: 0driveTrain: 0 wheels: 0 ]
Installing Wheel
Installing DriveTrain
Installing engine
Car3 [ engine: 1driveTrain: 1 wheels: 1 ]
Car4 [ engine: 0driveTrain: 0 wheels: 0 ]
Installing engine
Installing Wheel
Installing DriveTrain
Car4 [ engine: 1driveTrain: 1 wheels: 1 ]
Car5 [ engine: 0driveTrain: 0 wheels: 0 ]
Installing engine
Installing DriveTrain
Installing Wheel
Car5 [ engine: 1driveTrain: 1 wheels: 1 ]
Car6 [ engine: 0driveTrain: 0 wheels: 0 ]
Installing Wheel
Installing DriveTrain
Installing engine
Car6 [ engine: 1driveTrain: 1 wheels: 1 ]