Windows C++ Producer Consumer Threaded Example

Aim
The aim of this Windows C++ example is to demonstrate multi-threading, signalling, semaphores and mutexes using a simple producer and consumer example in C++ on Windows. This will use WINAPI functions such as CreateThread, WaitForMultipleObjects, CreateSemaphore, CreateMutex, ReleaseMutex and ReleaseSemaphore. Leave any questions or problems as comments and I will endeavour to answer them.

Assumptions
This article assumes that you have VC++ installed and configured. See Problems and Solutions Installing VC++ Express Edition, to install and run vcvars32.bat.

Versions used in this example
Sofware/Component
Image
Windows XP SP2
N/A
Visual Studio Express Editions 2008 VC++
N/A
Links to these files can be found here


Write and compile the application
  1. This application uses an integer queue. The producer adds values to this queue from 1 to 99 by pushing. Each time it adds a value it notifies a thread via a semaphore that there is something to consume. A consumer thread then consumes this value by popping it. Write the application and save is as thread.cpp.

     1. #include <windows.h>
     2. #include <iostream>
     3. #include <queue>
     4. #include <process.h>
     5. 
     6. using namespace std;
     7. 
     8. DWORD WINAPI threadConsumer(void*);
     9. DWORD WINAPI threadProducer(void*);
    10. 
    11. #define GETMYRAND() (int)(((double)rand()/(double)RAND_MAX)*100)
    12. 
    13. struct MyData{
    14.     queue<int> food;
    15.     bool producerfinished;
    16.     HANDLE mutex;
    17.     HANDLE controlsemaphore;
    18. };
    19. 
    20. int main (){
    21.     MyData mydata;
    22.     mydata.mutex = CreateMutex(NULL, false, NULL);
    23.     mydata.controlsemaphore = CreateSemaphore(NULL,0,2,NULL);
    24.     mydata.producerfinished=false;
    25. 
    26.     HANDLE handles[3];
    27. 
    28.     handles[0] = CreateThread(0,0,&threadConsumer, (void*)&mydata , 0,0);
    29.     handles[1] = CreateThread(0,0,&threadProducer, (void*)&mydata, 0,0);
    30.     handles[2] = CreateThread(0,0,&threadConsumer, (void*)&mydata, 0,0);
    31. 
    32.     WaitForMultipleObjects(3, handles, true, INFINITE); //"Join" trreads
    33.     
    34.     CloseHandle(mydata.mutex);
    35.     CloseHandle(mydata.controlsemaphore);
    36. }
    37. 
    38. DWORD WINAPI threadConsumer(void* lp){
    39.     MyData * md = (MyData*)lp;
    40.     
    41.     while(!md->producerfinished){
    42. 
    43.         Sleep(GETMYRAND());
    44. 
    45.         WaitForSingleObject(md->controlsemaphore,INFINITE); //Wait for producer;
    46. 
    47.         WaitForSingleObject(md->mutex, INFINITE); //Mutext Lock
    48.         while(md->food.size()>0){
    49.             char tmps[32];
    50.             sprintf(tmps, "\t%d\t(%d)\n",md->food.front(), GetCurrentThreadId());
    51.             cout<<tmps;
    52.             md->food.pop();
    53.         }
    54.         ReleaseMutex(md->mutex); //Mutex Unlock
    55. 
    56.     }
    57.     return 0;
    58. }
    59. 
    60. DWORD WINAPI threadProducer(void* lp){
    61.     MyData * md = (MyData*)lp;
    62. 
    63.     for(int i =0 ; i < 100; i++){
    64.         char tmps[32];
    65.         sprintf(tmps, "%d\t\t%d\n",i,GetCurrentThreadId());
    66.         cout<<tmps;
    67.     
    68.         Sleep(GETMYRAND());
    69. 
    70.         WaitForSingleObject(md->mutex, INFINITE); //Mutex Lock
    71.         md->food.push(i);
    72.         if(md->food.size()>0)
    73.             ReleaseSemaphore(md->controlsemaphore,1, NULL);
    74.         ReleaseMutex(md->mutex); //Mutex Unlock
    75.     }
    76.     md->producerfinished=true;
    77.     ReleaseSemaphore(md->controlsemaphore,2, NULL); //Release both threads
    78.     return 0;
    79. }
    Hide line numbers

    Line11: Randomly generate a sleep time between 0 and 100 milliseconds.

    Lines 28-30: Start all the threads giving the same MyData variable to them so the semaphore and the mutex is shared between all of them.

    Line 45 : Both consumer threads wait on the semaphore.

    Line 73: The producer thread triggers the semaphore allowing one waiting thread to proceed.

    Lines 47 & 54: Lock and unlock mutexes allowing only a single consumer thread to run this section of code. This is like a critical section or a 'synchronized' section java.

    Line 32: Wait for all the threads to finish processing. Like the join command in Linux.

  2. Open a prompt to the working directory and compile the code using the windows cl.exe compiler. You just need to include the pdh.lib.

    ..workspace\WinapiThreadExample>cl thread.cpp

  3. Now run the program in the command prompt. You should see something similar to what is shown below.
    
    80              2724
            79      (2996)
    81              2724
            80      (1108)
            81      (2996)
    82              2724
    83              2724
            82      (1108)
    84              2724
            83      (2996)
    85              2724
            84      (1108)
    86              2724
    
    The first column is the values produced by the producer, the second column is the value consumed by the consumer and the third column is the threadid of the consumers (in brackets) and producer. Note that there are 2 consumers in this example, hence the two consumer thread IDs 2996 and 1108 in this example

9 comments:

JoshuaC said...

This is really great! You get 100 million versions of this in java but nothing in C++ until now!

Anonymous said...

Yeah yeah yeah! I was looking for an example like this for days. I've only found java code but nothing about c++. And most of all...it works properly! Thank you

Anonymous said...

Thank you very much,,I was dying out without getting a single example in C++ of producer-consumer.It is very simple and understandable.

Anonymous said...

Thanks. Very good example
Good job

Regards
Sachin

Imanpreet said...

Hi, this is good. However, I believe producerfinished should have volatile type specifier, otherwise it is quite possible that you will never get out of the producer loop.

righteous said...

Strictly speaking you don't really need the volatile keyword for this example as we don't foresee any external process changing the variables.

I guess "volatile" is one of the most misunderstood keywords out there.

Imanpreet said...

@righteous: Producer and consumer are running in their own thread space. When compiler is compiling the code, it may optimize the consumer so that it always reads the local register value instead of the value that might have been modified by the producer.

righteous said...

Agreed! Better to be safe than sorry

Naviya Nair said...

Very interesting and good Explanation
ASP NET Training
ASP NET Training
ASP NET Online Training
C-Sharp Training
Dot Net Training in Chennai
Online .Net Training


MVC Training
WCF Training
Web-API Training
LINQ Training
Entity Framework
Training

Dot Net Interview Questions