65.9K
CodeProject is changing. Read more.
Home

VC++ Thread Synchronizatrion Using Events

starIconstarIcon
emptyStarIcon
starIcon
emptyStarIconemptyStarIcon

2.88/5 (8 votes)

Dec 17, 2006

CPOL

3 min read

viewsIcon

47660

This article demonstrates how to synchronize two threads using events.

Introduction

Interesting and challenging as it sounds, thread synchronization is still a major ouch! for an intermediate developer. This article, although not an expert insight into thread programming, serves as a primer for developers seeking to synchronize threads using events. There are many different synchronization objects (mutexes, semaphores, Critical Sections etc.) that can be used, but for the specific case that is going to be discussed, I think events serve the best purpose for synchronization. But as always, I am welcome to suggestions!

Understanding this article requires a basic understanding of multi-threaded programming.

The case in question: A main thread spawns two threads. It is required to execute each thread alternately, i.e., first Thread 1, then Thread 2, then again Thread 1, Thread 2 etc.

A Few Basics

The Event object is created using the CreateEvent function. The event can be either in a signaled or non-signaled state. Signaled means that the Event has released some waiting thread. Non-signaled means that all threads waiting for this event need to wait for it to become signaled. The SetEvent and ResetEvent functions can be used for putting the Event in the Signaled and Non-Signaled states, respectively. Basically, an event signifies to the process that something has happened. The waiting threads can then act accordingly once this event has occurred.

Let the Coding Commence

#include <windows.h>

#include <iostream.h>

/*************GLOBALS************/

HANDLE hThread1 , hThread2;
HANDLE hEvent1 , hEvent2;
int  g_nShared = 0; /* Global variable which is 
        going to be accessed by both Threads */

DWORD WINAPI Thread1(LPVOID lParam)
{
 while(1)
 {
  WaitForSingleObject(hEvent2,INFINITE);   
  ResetEvent(hEvent2);
  cout << "Thread1()::g_nShared = " << ++g_nShared << endl;
  SetEvent(hEvent1);   
 }
 return 0;
}

DWORD WINAPI Thread2(LPVOID lParam)
{
 while(1)
 {
  WaitForSingleObject(hEvent1,INFINITE);  
  ResetEvent(hEvent1);
  cout << "Thread2()::g_nShared = " << ++g_nShared << endl;
  SetEvent(hEvent2);  
 }
 return 0;
}

void main()
{
 // The Events which synchronize the 2 threads

 hEvent1 = CreateEvent(NULL,TRUE,FALSE,"Event1");
 hEvent2 = CreateEvent(NULL,TRUE,FALSE,"Event2");
 
 // Signal both the Events...so that they're up for grabs before 

 // the threads are born!!

 SetEvent(hEvent1); 
 SetEvent(hEvent2);
 
 // The Threads are Brought to Life !

 hThread1 = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)&Thread1,NULL,0,0);
 hThread2 = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)&Thread2,NULL,0,0); 
 
 while(1); // Don't let the Main Thread Die 

}

Explanation

Since there are two threads, we need to have two event objects to synchronize them, i.e., we need to make one thread wait till the other thread has finished executing a cycle. Each thread waits on an event object to be signaled. The other thread changes the state of this event object to signaled once it has completed executing one of its cycles.

The program starts with the main() function creating the two event objects, which are manual in nature. Hence, they need to be first put in the Signaled state before the threads are created. If you create and signal the events after creating the threads, then the program will wait indefinitely without any output on the console screen.

After this, the threads are spawned using the CreateThread function.

Thread1 executes first, since it is created first. It waits in the while loop for the hEvent2 object to get signaled. But, since hEvent2 is already signaled for the first execution of the loop, the next line is executed immediately. The next line puts the hEvent2 object in the non-signaled state by calling the function ResetEvent(). It then changes the value of the shared global g_nShared and then signals the hEvent1 object. Although this statement has no effect in the first execution of the loop, it plays an important role later. The point of execution returns to the beginning of the while loop where Thread 1 waits for the hEvent2 object to be signaled. This will get signaled by Thread 2 later. So, till that time, Thread 1 will have to wait.

Thread 2 is then executed since the hEvent1 object that it is waiting for is already signaled. Similar to Thread 1, it non-signals the hEvent1 object, changes the value of g_nShared, and then signals the hEvent2 object which causes Thread 1 to get executed again and Thread 2 to wait on the hEvent1 object to get signaled. This alternating cycles of loops of Thread 1 and Thread 2 continues till the program is terminated by the user.