Above is class diagram for a simple producer/consumer problem
#include <iostream> #include "MQ.h" #include "Thread.h" #include "Runnable.h" #include "ProductionTask.h" #include "ConsumptionTask.h" //File: Main.cpp using namespace examples; int main() { try { MQ q(10); ProductionTask producerTask(q); ConsumptionTask consumerTask(q); Thread t[2] = { producerTask, consumerTask }; //start the producer and consumer threads t[0].start(); t[1].start(); //wait 50000 ms before terminating the threads t[0].join(50000); t[1].join(50000); std::cout << std::endl << "Threads timed out!!" << std::endl; }catch(std::exception& e) { std::cerr << e.what() << std::endl; }catch(...) { std::cerr << "Unknown Exception" << std::endl; } return 0; }
Key Features
1. Threads are Tasks are decoupled. A thread can execute any task provided it implements the Runnable interface.
2. Windows threads are encapsulated using the technique demonstrated in the earlier blog post Encapsulating Windows Threads in C++ Objects
3. Two Tasks ProductionTask and ConsumptionTask share the common MQ instance q and try to execute add and remove operations on q concurrently. Synchronization is achieved here by means of Lock implemented using Windows CRITICAL_SECTION
4. The threads are allowed to run for 50 seconds before they are terminated. This is not a graceful termination and in real world applications may lead to unexpected results
#ifndef _Runnable_H_ #define _Runnable_H_ //File: Runnable.h #include <iostream> #include "NonCopyable.h" namespace examples { class Runnable { public: virtual ~Runnable(){} virtual void run() = 0; }; } #endif //_Runnable_H_
#ifndef _ConsumptionTask_H_ #define _ConsumptionTask_H_ //File: ConsumptionTask.h #include "Runnable.h" #include "MQ.h" namespace examples { class ConsumptionTask : public Runnable { public: ConsumptionTask(MQ &q); ~ConsumptionTask(); virtual void run(); private: MQ& m_queue; }; } #endif //_ConsumptionTask_H_
#include "ConsumptionTask.h" //File: ConsumptionTask.cpp using namespace examples; ConsumptionTask::ConsumptionTask(MQ &q) : m_queue(q) {} ConsumptionTask::~ConsumptionTask() {} void ConsumptionTask::run() { while(true) { m_queue.remove(); ::Sleep(550); } }
#ifndef _ProductionTask_H_ #define _ProductionTask_H_ //File: ProductionTask.h #include "Runnable.h" #include "MQ.h" namespace examples { class ProductionTask : public Runnable { public: ProductionTask(MQ &q); ~ProductionTask(); virtual void run(); private: MQ& m_queue; }; } #endif //_ProductionTask_H_
#include "ProductionTask.h" //File: ProductionTask.cpp using namespace examples; ProductionTask::ProductionTask(MQ &q) : m_queue(q) {} ProductionTask::~ProductionTask() {} void ProductionTask::run() { while(true) { m_queue.add("Object"); ::Sleep(500); } }
Read more ...