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 |
Write and compile the application
- 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. }
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.
- 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
- Now run the program in the command prompt. You should see something similar to what is shown below.
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 example80 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
8 comments:
This is really great! You get 100 million versions of this in java but nothing in C++ until now!
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
Thank you very much,,I was dying out without getting a single example in C++ of producer-consumer.It is very simple and understandable.
Thanks. Very good example
Good job
Regards
Sachin
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.
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.
@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.
Agreed! Better to be safe than sorry
Post a Comment