// // (Somewhat) simple program demonstrating the use of a "bounded // buffer" class of size N, with P producer threads and C consumer // threads. // Each producer thread will insert into the buffer M "messages" // (pairs of integers); consumer threads will read from the buffer // and print. // Consumer threads count how many buffer elements have been // consumed in all. // Main program waits for producers to finish and for the above // count to become zero, upon which it cancels the consumers. // // Command-line arguments are P, C, N, and M. // #include #include // has exit(), etc. #include // has usleep() #include // has rand() #include // has pair class #include // has greater_equal template fcn #include "pthreads-lock.h" // has lockObj class #include "pthreads-threadmgr.h" // has threadManager class #include "pthreads-bbuffer.h" // has boundedBuffer class #include "pthreads-sharedVar.h" // has sharedVar class lockObj outLock; // lock to permit thread-safe access // to cout, cerr void randomDelay(void); // function to introduce short delay, // to make things more interesting / // realistic typedef pair intpair; // ---- Class for producer thread ------------------------------------ // See function definitions below for comments about functions. class producerThreadObj { public: typedef producerThreadObj * ptr; producerThreadObj(const int myID, const int msgs, boundedBuffer * buffer); void run(void); private: int myID; // unique ID for thread int msgs; // count of messages to produce boundedBuffer * buffer; // pointer to shared bounded buffer }; // ---- Class for consumer thread ------------------------------------ // See function definitions below for comments about functions. class consumerThreadObj { public: typedef consumerThreadObj * ptr; consumerThreadObj(const int myID, boundedBuffer * buffer, sharedVar * msgsRemaining); void run(void); private: int myID; // unique ID for thread boundedBuffer * buffer; // pointer to shared bounded buffer sharedVar * msgsRemaining; // pointer to shared variable showing // how many messages are left to // consume bool reserveMsg(void); // function to "reserve" a message -- // check whether any left, decrement // count if so }; // ---- Main program ------------------------------------------------- int main(int argc, char* argv[]) { if (argc < 5) { cerr << "Usage: " << argv[0] << " nProducers nConsumers bufsize msgs\n"; exit(EXIT_FAILURE); } int nProducers = atoi(argv[1]); int nConsumers = atoi(argv[2]); int bufsize = atoi(argv[3]); int msgs = atoi(argv[4]); // Create shared bounded buffer and counter. boundedBuffer buffer(bufsize); sharedVar msgsRemaining(msgs * nProducers); // Create nProducers producerThreadObj objects, one for each thread, // to hold parameters. producerThreadObj::ptr * pThreadObjs = new producerThreadObj::ptr[nProducers]; for (int i = 0; i < nProducers; ++i) pThreadObjs[i] = new producerThreadObj(i, msgs, &buffer); // Create nConsumers consumerThreadObj objects, one for each thread, // to hold parameters. consumerThreadObj::ptr * cThreadObjs = new consumerThreadObj::ptr[nConsumers]; for (int i = 0; i < nConsumers; ++i) cThreadObjs[i] = new consumerThreadObj(i, &buffer, &msgsRemaining); // Create and start nProducers producer threads. threadManager ptMgr(nProducers, pThreadObjs); // Create and start nConsumers consumer threads. threadManager ctMgr(nConsumers, cThreadObjs); // Wait for all threads to complete. ptMgr.join(); ctMgr.join(); // Clean up and exit. for (int i = 0; i < nProducers; ++i) delete pThreadObjs[i]; delete [] pThreadObjs; for (int i = 0; i < nConsumers; ++i) delete cThreadObjs[i]; delete [] cThreadObjs; cout << "That's all, folks!\n"; return EXIT_SUCCESS; } // ---- Functions, etc., for producerThreadObj class ----------------- // Constructor. producerThreadObj::producerThreadObj(const int myID, const int msgs, boundedBuffer * buffer) { this->myID = myID; this->msgs = msgs; this->buffer = buffer; } // Member function containing code each thread should execute. void producerThreadObj::run(void) { // Insert messages with producer's ID # into buffer. for (int i = 0; i < msgs; ++i) { randomDelay(); buffer->put(intpair(myID, i)); outLock.lock(); cout << "Message (" << myID << ", " << i << ") produced\n"; outLock.unlock(); } } // ---- Functions, etc., for consumerThreadObj class ----------------- // Constructor. consumerThreadObj::consumerThreadObj(const int myID, boundedBuffer * buffer, sharedVar * msgsRemaining) { this->myID = myID; this->buffer = buffer; this->msgsRemaining = msgsRemaining; } // Member function containing code each thread should execute. void consumerThreadObj::run(void) { while (reserveMsg()) { // Consume messages until no more. intpair msg = buffer->get(); // Delay, then print it. randomDelay(); outLock.lock(); cout << "Message (" << msg.first << ", " << msg.second << ") consumed by " << myID << endl; outLock.unlock(); } } // Member function to check whether more messages to consume. // If current count > 0, decrements and returns true; // otherwise returns false. bool consumerThreadObj::reserveMsg(void) { msgsRemaining->lock(); bool returnVal = (msgsRemaining->value > 0); if (returnVal) --msgsRemaining->value; msgsRemaining->unlock(); return returnVal; } // ---- Miscellaneous other functions -------------------------------- void randomDelay(void) { // min, max delays (in microseconds) const int minDelay = 0; const int maxDelay = 1000000; int delay = minDelay + (int) (((double) rand() / (1.0 + (double) RAND_MAX)) * (maxDelay - minDelay)); usleep(delay); }