Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / programming / threads

Understanding Thread’s Synchronization Objects with a Real Life Example

5.00/5 (4 votes)
23 Jun 2013CPOL25 min read 34.6K   535  
This article will help you understand Thread`s Synchronization objects with a real life example.

Introduction

Lot of people find it difficult to code for multithreaded applications. When I first started to learn about threads, I was in a similar situation. Understanding threads and their synchronization conceptually is very important. Once the concepts are clear, it is very easy to code.

I will be trying to explain the concepts by using an example. This article assumes that you have basic knowledge of Windows thread APIs (at least how to create thread, pass data to thread function and wait for thread or threads to finish up). If you don’t have the basic knowledge, you can refer to the document Thread.docx in the document package. This document is taken from the web.

Using the Code

Before you start reading further down, please download the package. The package will have 2 folders, namely Files and Documents. The CPP files which end with test have tests for various scenarios. By default, the CriticalSectionTest will be run. For running other tests, you have to comment out the CriticalSectionTest and uncomment the Test which you want to run.

In the Document folder, you will find the Thread.docx which explains thread basic terminology and its usage. I recommend that you read this document first. It will give you an idea on what threads are and what is some basic terminology of threads.

In files folder, you will find all the files which are needed for this article. Create a Visual Studio 9 project and add all the files. Build it. Before you start the build, change the following setting in Visual Studio. Refer to image below:

Go to Project->Properties->General->Character Set.

Change from Use Unicode character Set to Use Multi-Byte Character Set. Refer to the Image below:

Click to enlarge image

In files, you will see cpp files which end with Test (for example MutexTest.cpp). These files contain the tests. The contents of all the files are commented. So if you want to run one of the tests, you have to uncomment the content of that file. You can run 1 test at a time. All the files content except that one which you want to run the test of should be commented.

Assuming you are ready with the project, let’s start to learn.

Let’s take the scenario that there is a bathroom. The people stand in the queue outside the bathroom and they want to use it one by one in an orderly fashion. There has to be an administrator who can monitor them so they use the bathroom one at a time. In order to do so, the administrator can define his own rules to create the order.

We will understand the threads and the synchronization process by deriving the analogy from the above example and we will code it too by using various synchronization techniques.

What is Resource?

As the name indicates, resource can be understood as something which can be used. Here in our case, it is the bathroom. Or in other words, it is whatever lies inside the door.

What are Threads?

Threads can be understood as a series of instructions. Like in our example, the person waiting in the queue is thread.

Synchronization Objects

Let’s say there is no administrator who can create rules on how the persons should use the bathroom. Imagine the chaos created by such a scenario. To maintain the order in the system, we need some kind of rule so that the resources (in our case, the bathroom) are used by the threads (in our case Persons) in a systematic and orderly way. To maintain the order, the administrator defines some kind of mechanism (here in our case, it's say key and bulb). The mechanism by which the things can be done in an orderly fashion can be understood as Synchronization Objects.

Let us first write the myThread class:

Check out the myThread.h and myThread.cpp files in the download.

  • Constructor sets the class member such as callback function, thread name, Parameters which is passed to thread function.
  • Execute() starts the thread and stores the handle of the thread in the class variable m_thread.
  • waitForThreadToFinish() waits for the thread to finish up .
  • waitForThreadsToFinish(int numOfThreads,HANDLE threadHandle[]) waits for all the threads whose handle we pass in this method in the form of handle array. This is made as static method because it is waiting for many threads to finish so we should be able to call it in a general way.
  • There are methods for the threads to resume, suspend the threads. There are other functions too which are self explanatory.

Different Administrators

Critical Section

Let’s say there is an administrator whose name is in the Critical Section. He has defined his own rules to maintain the order. Let’s see how he is maintaining the order:

  1. He stands in front of the queue of the persons and allows 1 person to go at a time to the bathroom.
  2. He only allows the person to go when he sees the bathroom is empty.

Let us write the myCriticalSection class. Checkout the myCriticalSection.h and myCriticalSection.cpp files in the download packet.

  • Constructor initializes the critical section. Initialization symbolizes the creation of the administrator.
  • Destructor deletes the critical section. Deletion symbolizes the deletion of administrator. When all the persons in the queue have used the bathroom, there is no need of administrator so deletion is required.
  • LockCriticalSection() method is used to lock the resources as someone is using them. This signifies that the administrator has locked the bathroom because someone is using it and he is standing in front to the queue of the persons and not allowing anyone to go inside.
  • UnLockCriticalSection() method is used to unlock the resource as the thread which was accessing it as it is done with that. This signifies that the person who was using the bathroom is done using that so the administrator unlocks that and allows the first person in the queue to enter.
  • CloseCriticalSection() deletes the administrator exclusively.

Till this point, we are ready with our Critical Section administrator and the Thread. Let’s try to simulate the bathroom scenario by writing the program.

Have a look at the CriticalSectionTest.cpp file. This has the main function.

  • We will create the administrator:
    C#
    myCriticalSection csBathroom("Bathroom");
    
  • We will create 4 threads, namely (Ram, Shyam, Raj and Hari) and run them. This signifies that we have created the 4 persons and they all want to go to bathroom. One thing here is very important that the thing which is responsible for putting all the threads in the queue in your operating system. We will store the handle of the threads in the handle array.
    C#
    myThread ShyamThread("Shyam", UseBathroomMonitoredByCS, (void*)"Shyam");
    ShyamThread.execute();
    handles[1] =  ShyamThread.getThreadHandle();
  • The function which each thread/person tries to run is UseBathroom(). Inside this function, the administrator locks the resources and then person/threads use the resources and once the person/thread finishes using the resources, the administrator unlocks the resource. Resource usage is defined by these 4 lines of code.
    C#
    x = count;
    x++;
    count = x;
    Sleep(5000);

    The first 3 lines are just incrementing the count. It is done in a strange way though. For now, just assume that it works that way. There will be an explanation at the end of this tutorial. We have put sleep for 5 second so that it can simulate the usage of bathroom by the person.

  • Finally, the system waits for all the threads to finish. It symbolizes that all the persons have used the bathroom.
    C#
    WaitForMultipleObjects(numThreads,handles,TRUE,INFINITE);
  • At last, we delete the administrator as his job is done.
    C#
    csBathroom.CloseCriticalSection();
  • Now, let’s run and see.
    Initializing the critical section
    Entering the critical section
    Ram is in the bathroom
    Ram is using the bathroom
    Ram will leave the bathroom
    Leaving the critical section Entering the critical section
    
    Shyam is in the bathroom
    Shyam is using the bathroom
    Shyam will leave the bathroom
    Leaving the critical section Entering the critical section
    
    Raj is in the bathroom
    Raj is using the bathroom
    Raj will leave the bathroom
    Leaving the critical section Entering the critical section
    
    Hari is in the bathroom
    Hari is using the bathroom
    Hari will leave the bathroom
    Leaving the critical section
    Deleting the critical section
    The no. of people used the bathroom are:4
    Deleting the critical section

As we can see, there is order in the system. It is possible that you run next time and the first person to use the bathroom can be Shyam. The queuing of threads is done by the operating system and hence it can be random. We don’t have any control on that.

Mutex

Let’s say there is an administrator whose name in Mutex. Mr. Mutex has defined his own rules to maintain the order. Let’s see how he is maintaining the order:

  1. He has a key to the bathroom. He gives the key to the person who is standing first in the row. As soon as the person/thread gets the key, he basically owns the mutex or the key.
  2. The person/thread uses the bathroom/resources and then gives the key back/releases the mutex to the administrator.
  3. The administrator gives this key again to the first person in the queue.
  4. The Mutex is not so smart and has a very bad memory. So he keeps a register with him and always writes whether or not the key is with him. If he decided to give the key to person he marks false in the register and then gives it to the first person waiting in the queue. The marking of false can be seen as the signal to the person that key is available for grab. When the key is with him, he marks TRUE in register .

Let us write the myMutex class. Checkout the myMutex.h and myMutex.cpp files in the download packet.

  • Constructor initializes the mutex. Initialization symbolizes the creation of the administrator.
    • It is done by system method CreateMutex(). This method takes 3 parameters:
      1. Security attributes
      2. If this value is TRUE and the caller created the mutex, the calling thread obtains initial ownership of the mutex object. Otherwise, the calling thread does not obtain ownership of the mutex. Set to TRUE if you want to initially own the mutex. If set to FALSE, the first thread which asks for permission will get the ownership.
      3. The name of the mutex, or 0 if nameless.

o Return Values:

    1. If the function succeeds, the return value is a handle to the newly created mutex object.
    2. If the function fails, the return value is NULL. To get extended error information, call GetLastError.
    3. If the mutex is a named mutex and the object existed before this function call, the return value is a handle to the existing object,           GetLastError returns ERROR_ALREADY_EXISTS, bInitialOwner is ignored, and the calling thread is not granted ownership. However, if the caller has limited access rights, the function will fail with ERROR_ACCESS_DENIED and the caller should use the OpenMutex function.

o If the 2nd parameter is false, this means the mutex is in the signaled state and first person in the line can grab it.

  • Destructor deletes the mutex. Deletion symbolizes the deletion of administrator. When all the persons in the queue have used the bathroom, there is no need of administrator so deletion is required.
  • LockMutex() method is used to lock the resources as someone is using them. This signifies that the administrator has locked the bathroom because someone is using it and he is standing in front to the queue of the persons and not allowing anyone to go inside. This is done by WaitForSingleObject() with infinite time. WaitForSingleObject() waits for the handle (in this case, this is mutex handle) and acquires it.
  • UnlockMutex() method is used to unlock the resource as the thread which was accessing it is as it is done with that. This signifies that the person who was using the bathroom is done using that so administrator unlocks that and allows the first person in the queue to enter. This is done by ReleaseMutex() method. ReleaseMutex() releases the mutex.
  • CloseMutex() deletes the administrator exclusively.
  • The OpenMutex function enables multiple processes to open handles of the same mutex object. The function succeeds only if some process has already created the mutex by using the CreateMutex function. The calling process can use the returned handle in any function that requires a handle to a mutex object, such as the wait functions, subject to the limitations of the access specified in the first parameter of the function.

o It takes 3 parameters:

  1. The access to the mutex object. Only the SYNCHRONIZE access right is required to use a mutex; to change the mutex's security, specify MUTEX_ALL_ACCESS. The function fails if the security descriptor of the specified object does not permit the requested access for the calling process. For a list of access rights, see Synchronization Object Security and Access Rights.
  2. If this value is TRUE, processes created by this process will inherit the handle. Otherwise, the processes do not inherit this handle.
  3. The name of the mutex to be opened. Name comparisons are case sensitive.

o Return Values:

    1. If the function succeeds, the return value is a handle to the mutex object.
    2. If the function fails, the return value is NULL. To get extended error information, call GetLastError.
    3. If a named mutex does not exist, the function fails and GetLastError returns ERROR_FILE_NOT_FOUND.

We are ready with our Mutex administrator. Let’s try to simulate the bathroom scenario by writing the program.

Have a look at the MutexTest.cpp file. This has the main function. The code is self explanatory as we have done the explanation for Critical Section. The same kind of output is expected.

Semaphore

So now it’s time to get introduced to a new administrator whose name is Semaphore. He has defined the following rules:

  1. He has a key to the bathroom. He gives the key to the person who is standing first in the row. As soon as the person/thread gets the key, he basically owns the semaphore or the key.
  2. The person/thread uses the bathroom/resources and then gives the key back/releases the semaphore to the administrator.
  3. The administrator gives this key again to the first person in the queue.
  4. The Semaphore is smarter than Mutex. He keeps the count of the keys which is with the Semaphore. So when he is having the key, he increases the counter by 1. He has a counter on his table which indicates the count. When he gives the key to the person, he decreases the count by 1. When the counter is showing more than 0, it means the semaphore is up for the grab and the first person in the queue can grab it.
  5. Let us consider a scenario where there are more than 1 bathrooms (let’s say 3) and we have to create the order for the usage of multiple bathrooms. In this scenario, our semaphore administrator can manage it by following its rules:
    1. a. The initial count in the counter will be showing 3.
    2. b. He gives one key to the 1st person in the queue and decreases the counter by 1. So when 3 people are in the bathrooms the counter is 0, hence others have to wait.
    3. c. As soon as any person comes out and hands over the key, the counter gets increased and the first person can take the key and use the bathroom.
  6. Till now, you will have figured out that if Semaphore can handle only 1 key, it is behaving like Mutex.
  7. Semaphores can be divided into 2 categories:
    1. Mutex Semaphores/Binary semaphores.
      1. If the value of counter can be only 0 and 1, it is called binary semaphores
      2. If we replace the counter with the Boolean variable, it becomes mutex semaphores.
      3. Both can be used interchangeably.
    2. Counter Semaphores
      1. The maximum value of the counter can be more than 1 as we discussed above.

Let us write the mySemaphore class. Checkout the mySemaphore.h and mySemaphore.cpp files in the download packet.

We will write this class with bit more details to it. Like we will initialize the security attributes. We will write the constructor which can create and open up the semaphores. Memory allocation/deletion is done through HeapAlloc() and HeapFree() method. This will give the real feel of what the actual class should look like.

• Constructor initializes the semaphores and if it already exists, it tries to open it. Initialization symbolizes the creation of the administrator. It can open up the semaphore if it already exists.

  • This is done by method CreateSemaphore(). It creates or open the semaphore
  • It takes 4 arguments:
    1. Security attributes.
    2. The initial value of the semaphore count. The initial value here given is 1 - it means that the semaphore is signalled and first thread in the queue.
    3. The maximum value of the semaphore count.
    4. The name of the semaphore, or 0 if the semaphore is nameless.
    • Return Values: Returns a handle to the semaphore, or NULL on error. If a semaphore of the specified name already exists, then GetLastError() will return ERROR_ALREADY_EXISTS but the handle will be valid.
    • Opening of existing semaphores is done by OpenSemaphore(). Details can be found on the web.
  • Destructor deletes the semaphores. Deletion symbolizes the deletion of administrator. When all the persons in the queue have used the bathroom, there is no need of administrator so deletion is required.
  • LockSemaphore (BOOL wait=TRUE) method is used to lock the resources as someone is using them. This signifies that the administrator has locked the bathroom because someone is using it and he is standing in front to the queue of the persons and not allowing anyone to go inside. It takes a Boolean argument. If this is true, it waits for infinite time and in case of false, it does not wait for any time. This is done by WaitForSingleObject() with infinite time. WaitForSingleObject() waits for the handle (in this case, this is semaphore handle) and acquires it.
  • UnlockSemaphore () method is used to unlock the resource as the thread which was accessing it as it is done with that. This signifies that the person who was using the bathroom is done using that so administrator unlocks that and allows the first person in the queue to enter. This is done by ReleaseSemaphore () method. ReleaseSemaphore () releases the semaphore.
  • CloseSemaphore () deletes the administrator exclusively.
  • Other methods are fairly straightforward to understand.

We are ready with our Semaphore administrator. Let’s try to simulate the bathroom scenario by writing the program.

Have a look at the SemaphoreTest.cpp file. This has the main function. The code is self explanatory as we have done the explanation for Critical Section. The same kind of output is expected. If you have understood the concepts of semaphore, try creating the program for 3 bathroom and 10 people.

Event

Let us learn about a new administrator called Event. He has defined the following rules:

  1. He has put a bulb on the top of bathroom door. It will act as a signal for the persons standing the queue.
  2. Once a person is inside the bathroom, the bulb is red saying to others to wait.
  3. When the person comes out of the bathroom, the signal becomes green indicating to the persons in the queue that the resource is up for grabs.
  4. Event administrator is smart and somewhat lazy so he asks threads to do the signaling (turning red to green when they are done using the bathroom) and he sits happily watching them.
  5. If he delegates the task of turning the bulb from green to red to the threads this way, it is called manual signaling. Otherwise, if the administrator is doing it, it is called automatic signaling.
    1. Manual Reset Events:
      1. As we know, there are no free lunches in the world, hence when the threads are handling signaling (green to red only) themselves, all the threads in the queue try to grab the bathroom as soon as the bulb turn green.
      2. The thread has the responsibility to turn the signal red to green and green to red. Green to red is done by resetEvent() and red to green is done by setEvent().
    2. Auto Reset Events:
      1. As soon as the bulb turn from red to green, only 1 thread in the queue (1st one) tries to grab the resource.
      2. The thread has the responsibility to turn the signal red to green. Green to red is done by resetEvent() and red to green is done by setEvent().
  6. Auto reset event is useful when one thread has to coordinate with one another thread. Manual reset event will be useful when one thread needs to coordinate with any number of threads.
  7. Let us write the myEvent class. Checkout the myEvent.h and myEvent.cpp files in the download packet.
  • Constructor initializes the event. It takes three parameters. First one is event name, second is Boolean to indicate whether the event is manual reset or auto reset. Third is for the initial signal state.
    • This is done by CreateEvent(). Creates an event, or opens one if the name specified already exists.
    • It takes 4 arguments:
      1. Security attributes
      2. TRUE for manual reset, FALSE for automatic reset.
      3. The initial state of the event: TRUE if signaled.
      4. The name of the event, or 0 if nameless.
    • Return values: Returns a handle to the event, or 0 on error. If an event of the specified name already exists, then GetLastError will return  ERROR_ALREADY_EXISTS but the handle will be valid.
  • Destructor deletes the event. Deletion symbolizes the deletion of administrator. When all the persons in the queue have used the bathroom, there is no need of administrator so deletion is required. This is done by closehandle() message
  • setEvent () method is used to make signal from red to green.
  • resetEvent () method is used to make signal from green to red.
  • pulseEvent() method details can be found on web.
  • closeEvent () deletes the administrator exclusively.
  • Other methods are fairly straightforward to understand.

We are ready with our Event administrator. Let’s try to simulate the bathroom scenario by writing the program.

  1. We will try to simulate the bathroom scenario by using auto reset events. Have a look at AutoEventTest.cpp. The output is as expected and each person uses the bathroom one by one.
    1. Let’s see what the method UseBathroomMonitoredByAutoEvent does.
    2. First, we wait for the signal by using WaitForEvent();
    3. As soon as the signal is green, the 1st thread in the queue will try to grab the resource (it is explained earlier).
    4. Use the resources:
      Use Resources
      x = count;
      x++;
      count = x;
      Sleep(5000);
    5. Set the signal to green, so other can be used.
    6. We will see the synchronized use of the bathroom here.
    7. Since initial state is red or non signaled, we change it to green or signaled in main manualEventBathroom.setEvent();
    8. Other things are kept the same as for earlier examples.
    9. This type of auto reset event can be used when one thread has to signal only 1 thread. That is why this is helping us in synchronizing.

    We will try to simulate the bathroom scenario by using manual reset events. Have a look at ManualEventTest.cpp. The output which we get is something like this:

    Initializing the Event
    Giving the signal
    The signal has been given, Acquire the resource The signal has been given, 
    Acquire the resource The signal has been given, 
    Acquire the resource The signal has been given, Acquire the resource
    Resetting the Event  Resetting the Event  Resetting the Event  Resetting the Event
    RamShyamRajHari is in the bathroom is in the bathroom is in the bathroom is in the bathroom
    RamShyamRajHari is using the bathroom is using the 
    bathroom is using the bathroom is using the bathroom
    RamShyamRajHari will leave the bathroom will leave the 
    bathroom will leave the bathroom will leave the bathroom
    Giving the signal Giving the signal Giving the signal Giving the signal
    Deleting the Event
    The no. of people used the bathroom are:4
    Deleting the Event
  2. Let’s see what the method UseBathroomMonitoredByManualEvent does.
    1. First, we wait for the signal by using WaitForEvent();
    2. As soon as the signal is green, all threads in the queue will try to grab the resource (it is explained earlier).
    3. As soon as we get the signal, we make the signal to red:
      C#
      manualEventBathroom.resetEvent();
    4. Use the resources
      Use Resources
      x = count;
      x++;
      count = x;
      Sleep(5000);
    5. Set the signal to green, so other can be used.
    6. We are not seeing the synchronized usage. It very random.
  3. Now the question arises in what condition we should use the manual reset event. This can be used when one thread wants to signal many other threads. In this case, all the threads/persons are catching the signal and trying to grab the bathroom.
  4. Since initial state is red or non signaled, we change it to green or signaled in main.
    C#
    manualEventBathroom.setEvent();
  5. Other things are kept same as for earlier examples.

Till now, you would have figured out that events are different from locks (critical section, mutex and semaphores). Events are used for signaling. We however use to synchronize the threads by using correct mechanism. As we have seen, auto reset events can be used to synchronize the threads. However still we have to get the clarity on how to use the manual reset events.

Let us see now how we can use the manual reset events. We will understand this scenario by solving a problem. Here is the problem:

Assume there is a race going on.

  • There are three participants (A, B, C).
  • A finishes the race in 6 sec, B finishes in 7 sec and C finishes in 8 sec.
  • A gives a signal after 1 sec, B gives after 2 and C gives after 3 sec after the beginning of the race.
  • We have to simulate the scenario in such a way that we are able to catch all the signals and we are able to tell when all the participants have finished the race.
    Have a look at ManualEventTest1.cpp file.
  • The three persons who participates in the race is depicted by three threads (RunParticipantA, RunParticipantB and RunParticipantC).
  • We have created three manual events (Event1, Event2 and Event3) for each participant A, B and C respectively. All events initial signaled state is non signaled.
  • Let’s have a look at how participant A goes about his race. He starts the race. Gives the signal after 1 second.
    • The wait of 1 second is simulated by sleep function.
    • Then event is reset (no need of it, however doing so as the initial state is not signaled).
    • Then event is signaled. This is done by setEvent().
    • The threads wait for 5 more second to finish up.
  • In the main function, we collect all the handles of the events and wait for all of them to happen.
    • This is done by WaitForMultipleObjects( 3, Handles_Events, TRUE, 6000);
  • In the main, we also collect the handle of threads and wait for all of them to finish or all of the persons to end the race.
  • The output will be like this:
    Initializing the Event
    Initializing the Event
    Initializing the Event
    RACE STARTED
    Participant A started the race
    Participant C started the race
    Participant B started the race
    Waiting for all the events to get signalled
    Resetting the Event
    Participant A will give the signal...
    Giving the signal
    Deleting the Event
    Resetting the Event
    Participant B will give the signal...
    Giving the signal
    Deleting the Event
    Resetting the Event
    Participant C will give the signal...
    Giving the signal All events are signalled
    Deleting the Event
    Participant A has finished the race
    Participant B has finished the race
    Participant C has finished the race
    RACE FINISHED
    Deleting the Event
    Deleting the Event
    Deleting the Event

By looking at the output, we have simulated the scenario which we have mentioned in the problem statement above.

Let us try to write an event class which is smarter than the event class which we have written. Let us call this mySmartEvent class. Have a look at mySmartEvent.h and mySmartEvent.cpp file.

What is smart about this event class?

Whenever the object of this class is created, an event is created and we start a thread which monitors that event state. As the event gets signaled, this thread queues up a callback for the execution. Let us try to see the class functions and their implementations.

  • Constructor takes three parameters. First one is the event name; second one is the callback function name and last is a Boolean which governs whether or not the callback should be queued.
    • The manual reset event is created whose initial state is not signaled.
    • The callback function is set to a class member variable which will be used afterwards.
    • A thread is started which will call the method ThreadProc(). The interesting thing to notice is that we pass the this pointer to the thread arguments in here. This is done because we will require the event object in the Thread Proc.
  • ThreadProc waits for the event object to be signaled state.
    • This is done by WaitForSingleObjectEx().
  • The callback function is queued by using QueueUserAPC().
  • We have made the thread sleep infinitely to make sure the callback gets called.
  • Other functions are the same as in earlier myEvent class.

Now let us try to test this class. Have a look at SmartEventTest.cpp.

  • We have defined a callback which increments a static integer and prints it.
  • Create a smart event object.
    • As soon as we create this object, a thread starts to wait for that event to get signaled.
  • Inside the main(), we signal the event.
    • The thread sees that the event is signaled and queues the callback for execution.
  • To double check, we check whether or not the event has been signaled by using eventSmart1.WaitForEvent(0). We use 0 waiting time because we know event has already occurred. We are not waiting here, but we are rather just checking.
  • The output of the program will be:
    Initializing the Event
    Signaling the event
    Giving the signal The state of the specified event object is signaled.
    Waiting for event to happen schedule the callback...
    The signal has been given, Acquire the resource Hello I am callback and value if ii is
    1The event has happened
    Deleting the Event

What is the difference between WaitForSingleObjectEx and WaitForSingleObject?

The function WaitForSingleObjectEx is similar to the WaitForSingleObject function except that it takes an additional boolean parameter whose value decides whether the calling thread will be in an alertable wait state or not.

If this boolean parameter is set to TRUE, the function will return whenever a completion routine or asynchronous procedure call (APC) is queued. An APC is queued when the QueueUserAPC API is called and a completion routine queuing happens when the ReadFileEx or WriteFileEx APIs are completed. When the ReadFileEx or WriteFileEx APIs are called, a completion function name is passed as parameter. When the actual read or write completes, the wait function returns and the completion function will be called by the system, provided the thread is in an alertable wait state.

Let’s think of an example to better understand what the alertable wait state means. Assume you are doing inter-process communication (IPC) using named pipes or mailslots. Further assume that the client application has to wait for a thread to complete. At the same time, whenever a message arrives at the pipe, some operations have to be performed. This is possible by using the WaitForSingleObjectEx API and giving the handle of the thread as the first parameter and setting the alertable parameter to TRUE.

Summary

With this, I will be finishing up this article. Just to recap, we have learn about the basic synchronization objects(Critical Section, Mutex and Semaphores). We have also learn about Events. We have seen how the events are different from locks and how they can be used to synchronize the threads. We have also seen how to create a Smart Event class which will be having a callback function called if specified.

Please forgive my grammatical mistakes. Your suggestions, constructive comments are appreciated.

Points of Interest

Threads are not so difficult to deal with. The concepts of threads are more or less the same everywhere across all operation systems. Learning basic concepts always saves a lot of time afterwards.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)