Click here to Skip to main content
Click here to Skip to main content

Thread Synchronization for Beginners

, 11 Aug 2004 CPOL
Rate this:
Please Sign up or sign in to vote.
This article explains how to use Simple Thread synchronization in Win32 and MFC.

Introduction

In my previous article, we discussed simple multithreaded programming in C, Win32 and MFC. Now, we see simple thread synchronization with Win32 API and MFC.

What is Thread Synchronization?

In a multithreaded environment, each thread has its own local thread stack and registers. If multiple threads access the same resource for read and write, the value may not be the correct value. For example, let's say our application contains two threads, one thread for reading content from the file and another thread writing the content to the file. If the write thread tries to write and the read thread tries to read the same data, the data might become corrupted. In this situation, we want to lock the file access. The thread synchronization has two stages. Signaled and non-signaled.

The signaled state allows objects to access and modify data. The non-signaled state does allow accessing or modifying the data in the thread local stack.

Thread Synchronization methods:

Many of the thread synchronization methods are used to synchronize multiple threads. The following methods are used to synchronize between objects.

Thread Synchronization on different processes:

Event:

Event is a thread synchronization object used to set the signaled or non-signaled state. The signaled state may be manual or automatic depending on the event declaration.

Mutex:

Mutex is the thread synchronization object which allows to access the resource only one thread at a time. Only when a process goes to the signaled state are the other resources allowed to access.

Semaphore:

Semaphore is a thread synchronization object that allows zero to any number of threads access simultaneously.

Thread Synchronization in same process:

Critical Section

The critical section is a thread synchronization object. The other synchronization objects like semaphore, event, and mutex are used to synchronize the resource with different processes. But, the critical section allows synchronization within the same process.

The given difference is the main difference between the thread synchronization objects. The other differences between the thread synchronization are the following:

Win32 Wait Functions:

The Wait family of functions are used to wait the thread synchronization object while a process completes. The widely used functions are WaitForSingleObject and WaitForMultipleObjects functions. The WaitForSingleObject function is used for waiting on a single Thread synchronization object. This is signaled when the object is set to signal or the time out interval is finished. If the time interval is INFINITE, it waits infinitely.

The WaitForMultipleObjects is used to wait for multiple objects signaled. In the Semaphore thread synchronization object, when the counters go to zero the object is non-signaled. The Auto reset event and Mutex is non-signaled when it releases the object. The manual reset event does affect the wait functions' state.

MFC Lock/Unlock Resource:

The MFC CMutex, CCriticalSection, CSemaphore, and CEvent classes are used to synchronize the threads in Microsoft Foundation Class library.

The CSingleLock and CMultiLock are used to control the access to the resources in multithread programming. The CSingleLock and CMultiLock classes have no base class. CSingleLock is used to lock the single synchronization object at a time. CMultiLock is used to control more than one thread synchronization objects with a particular time interval. The CSingleLock/CMultiLock Lock and Unlock member functions are used to the lock or release the resource.

The CSingleLock and CMultiLock constructors use the CSyncObject object for locking and unlocking the resource. All the Thread Synchronization classes are derived from CSyncObject base class. So, the constructor has any one of the thread synchronization classes derived from CSyncObject. CSingleLock IsLocked member is use to find if the object is locked already or not.

The CMultiLock class is used to control the access to the resources in multiple objects. The CMultiLock constructor has an array of CSyncObject objects and the total number of counts in the thread synchronization classes. The IsLocked member function is used to check the particular synchronization object state.

Thread Synchronization Objects:

Critical Section:

The Critical section object is same as the Mutex object. But, the Mutex object allows synchronizing objects across the process. But the Critical section object does not allow synchronization with different processes. The critical section is used to synchronize the threads within the process boundary.

It is possible to use Mutex instead of critical section. But, the critical section thread synchronization object is slightly faster compared to other synchronization objects. The critical section object synchronizes threads within the process. Critical section allows accessing only one thread at a time.

Win32 Critical Section Object:

The process allocates memory for the critical section using the CRITICAL_SECTION structure. The critical section structure declared in the Winnt.h is as follows:

typedef struct _RTL_CRITICAL_SECTION {
    PRTL_CRITICAL_SECTION_DEBUG DebugInfo;
    //  The following three fields control entering
    //  and exiting the critical  section for the resource
    LONG LockCount;
    LONG RecursionCount;
    HANDLE OwningThread;   // from the thread's ClientId->UniqueThread
    HANDLE LockSemaphore;
    DWORD SpinCount;
} RTL_CRITICAL_SECTION, *PRTL_CRITICAL_SECTION;

In critical section, we allocate memory for CRITICAL_SECTION structure and initializes the critical section. The IniailizeCriticalSection and InitializeCriticalSectionAndSpinCount are used to initialize the critical section. If we initialize the critical section, then only, we use any one of the EnterCriticalsection, TryEnterCriticalSection, or LeaveCriticalSection functions. The EnterCriticalsection function is used to enter the critical section, and TryEnterCriticalSection to enter the critical section without blocking. LeaveCricalSection is used to leave the critical section.

If any of the other synchronization object names is same as Critical section object, the Critical section object waits for the ownership infinitely. The Critical section object does not allow moving or copying the object. If we have to synchronize the thread on different processes, use Mutex object. DeleteCriticalSection function releases all the critical section objects. After calling the DeleteCriticalSection, it is not possible to call EnterCriticalsection or LeaveCriticalSection.

Example:

CRITICAL_SECTION m_cs;

//Initilize the critical section
InitializeCriticalSection(&m_cs);

The two threads try to access the same variable. The global variable g_n tries to access two threads. The global m_cs CRITICAL_SECTION structure is used to synchronize the two threads.

UINT ThreadOne(LPVOID lParam)
{
    // 
    // Lock the Critical section
    EnterCriticalSection(&m_cs);

    // Some Process

    //Release the Critical section
    LeaveCriticalSection(&m_cs);

return 0;
}

Thread two:

UINT ThreadTwo(LPVOID lParam)
{
// Lock the Critical section
    EnterCriticalSection(&m_cs); 

    // Some Process

    //Release the Critical section
    LeaveCriticalSection(&m_cs);

    // return the thread
    return 0;
}

MFC Critical Section object:

The CCriticalSection class provides the functionality of critical section synchronization object. The default constructor is used to construct the critical section object. The Lock and Unlock functions are used to control the resource access in the synchronization object.

The CRITICAL_SECTION's m_sect data member allows initializing the CRITICAL_SECTION structure. The Lock function overloaded in two forms. The Lock function without any arguments is used to lock the resource. The other form of Lock function needs the number of milliseconds to wait. All the critical section members are declared in afxmt.inl file as inline functions.

Example:

// Global Critical section
CCriticalSection c_s;
int g_C;

//////////////////Thread One ///////////////////////

UINT ThreadFunction1(LPVOID lParam)
{

    // Create object for Single Lock
    CSingleLock lock(&c_s);

    // Lock
    lock.Lock();

    // Process

    // Unlock
    lock.Unlock();

    // return 
    return 0;
}


////////////Thraed 2////////////////////
UINT ThreadFunction2(LPVOID lParam)
{

    // Single Lock Constract Critical Section
    CSingleLock lock(&c_s);

    // Lock
    lock.Lock();

    // Process

    // Unlock
    lock.Unlock();

    //return
    return 0;
}

Event:

Event is the thread synchronization object to set signaled state or non-signaled state. The Event has two types. They are manual reset event and auto reset event.

The manual event has signaled user set to non-signaled state, uses ResetEvent function manually. The auto reset event automatically occurs when the object is raised to the non-signaled state. The event thread synchronization object is used to synchronize the particular event entered in the thread. The Event object is used to set the operating system kernel flag in the thread.

Win32 Event Object:

The CreateEvent function is used to create the event thread synchronization object. The manual or auto reset event choice is mentioned at the CreateEvent function parameter initialization. The Wait family functions (WaitForSingleObject, WaitForMultipleObjects) are use to wait when a particular event occurs. The group of objects waits for the events: the single object signaled or the entire events are signaled in the thread.

CreateEvent function is used to create manual or auto reset events. This function is used to create named and unnamed event objects. SetEvent function is used to set the event object to signal state. The ResetEvent function is used to set the event object to non-signaled state. If the function is successful, it returns the handle of that event. If the named event is already available, the GetLastError function returns the ERROR_ALREADY_EXISTS flag. If the named event is already available, the OpenEvent function is used to access the event previously created by the CreateEvent function.

Example:

// Handle for Event
HANDLE g_Event; 
// Create a manual-reset event object with  no security attributes 

g_Event = CreateEvent(  NULL,      TRUE,    TRUE,  "Event"     ); 

ResetEvent(g_Event);

// Do Process

SetEvent(g_Event);

MFC Event Object:

Event is useful for waiting if something happens (or an event occurs). CEvent class is used for event object functionality. The CEvent class is derived from the abstract CSyncObject Class. The CSyncObject is derived from the CObject mother class. The CSingleLock or CMultiLock constructor use one of the CSyncObject derived classes. The CEvent constructor specifies the manual or auto reset event options.

MFC CEvent class provides the constructor with default arguments. If we want to control our needs, we set the Ownership for the event object, manual or auto reset event flag, the name of the event object (if named event), and the security attributes. If the name matches with that of an existing event object, check the type of the object. If that object is an event, the CEvent constructor simply replies the handle of the previous event. If the name exists for any of the other synchronization object, the CEvent constructor gives an error message.

The Manual event object signaled/non-signaled uses SetEvent or ResetEvent function .The Auto Reset event occurs when it releases any one of threads in the event object. We don't use CSyncObject directly. Because, the CSyncObject is an abstract base class. All the event members are declared in the afxmt.inl file as inline functions.

Mutex Synchronization Object:

Mutex is the synchronization object used to synchronize the threads with more than one process. The Mutex is as the name tells, mutually exclusive. The Mutex object allows accessing the resource single thread at a time.

Win32 Mutex Object:

The CreateMutex function is used to create the Mutex object. In the CreateMutex function, we initialize the named Mutex or unnamed Mutex, and set the ownership to true or false arguments.

If we create two-named Mutex using CreateMutex function within the same process, the second CreateMutex function returns error. If we create two or more Mutex objects on different processes, with the same name, when we call first time, the CreateMutex function creates the Mutex. The other CreateMutex function returns the handle of the previous Mutex object.

The OpenMutex function is used to open an existing Mutex using the supplied Mutex name. The ReleaseMutex is used to release a Mutex object. The threads wait in first in first out order for taking the ownership for the waiting threads. If we try to take the ownership twice, deadlock occurs. We the call ReleaseMutex, and then try to take the ownership.

Example

HANDLE g_Mutex;
DWORD dwWaitResult;

// Create a mutex with initial owner.
g_Mutex = CreateMutex( NULL, TRUE, "MutexToProtectDatabase");
 
// Wait for ownership
dwWaitResult = WaitForSingleObject( g_Mutex, 5000L);

// Check takes ownership

// Release Mutex
ReleaseMutex(g_Mutex))

MFC Mutex Object:

The MFC CMutex class is used to control the Mutex objects. The CMutex constructor has three parameters. We can specify the name of the Mutex as a parameter. If this is null, an unnamed Mutex is created. The Security attributes are used to set the security attributes for the Mutex object. If the name already exists for a Mutex object, the constructor simply returns the existing Mutex object. If the name exists for some object, the constructor fails.

To control resource access for single Mutex object, use CSingleLock class. If you wish to control multiple Mutex objects, the CMultiLock is used to control the access to resources in multithreaded programming.

// Global Mutex Object
CMutex g_m;
int g_C;

UINT ThreadFunction1(LPVOID lParam)
{

    // Create object for Single Lock
    CSingleLock lock(&g_m);

    lock.Lock();

    // Process

    lock.Unlock();

    // return 
    return 0;
}


UINT ThreadFunction2(LPVOID lParam)
{

    // Single Lock Construct Mutex
    CSingleLock lock(&g_m);

    lock.Lock();

    // Process

    lock.Unlock();

    //return
    return 0;
}

Semaphore Thread Synchronization Object:

Semaphore is a thread synchronization object that allows accessing the resource for a count between zero and maximum number of threads. If the Thread enters the semaphore, the count is incremented. If the thread completed the work and is removed from the thread queue, the count is decremented. When the thread count goes to zero, the synchronization object is non-signaled. Otherwise, the thread is signaled.

Win32 Semaphore Synchronization Object:

The CreateSemaphore function is used to create a named or unnamed semaphore thread synchronization object. The initial count and maximum count is mentioned in the CreateSemaphore function. The count is never negative and less then the total count value. The WaitForSingleObject waits for more than one object in semaphore object.

The WaitForMultipleObjects function is non-signaled when all the objects are returned. The OpenSemaphore function is used to open an existing handle to a semaphore object created within the process or another process. The Releasesemaphore function is used to release the semaphore from the Thread synchronization queue. If the CreateSemaphore function has created the same named Thread synchronization object within the process, the CreateSemaphore returns 0. The GetLastError function is used to retrieve the reason for the failure.

Example:

HANDLE g_Semaphore;

// Create a semaphore with initial and max.
g_Semaphore = CreateSemaphore(  NULL,   4, 4,  NULL);  

DWORD dwWaitResult; 
//take ownership
dwWaitResult = WaitForSingleObject(   g_Semaphore, 0L);

// Check the ownership

// Release Semaphore
ReleaseSemaphore(  g_Semaphore,  1,  NULL) ;

MFC Semaphore Synchronization Object:

The CSemaphore class allows us to create the CSemaphore object. The CSemaphore class is derived from the abstract CSyncObject base class. The CSemaphore class constructor is used to specify the count and maximum number of resource access.

The virtual destructor is used to delete the CSemaphore class object without affecting the CSyncObject base class. The Unlock function is used to unlock the resources.

Conclusion:

Thread Synchronization is used to access the shared resources in a multithread environment. The programmer decides the situation for when to use the synchronization object efficiently. The MFC Thread Synchronization classes internally call the Win32 API functions. The MFC Thread Synchronization classes wrap many of the functionalities form the Windows environment.

License

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

Share

About the Author

R.selvam
Software Developer (Senior)
India India
Selvam has worked on several technologies like Core Java, VC++, MFC, Windows API and Weblogic server. He takes a lot of interest in reading technical articles and enjoy writing them too. He has been awarded as a Microsoft Community Star in 2004, MVP in 2005-06, SCJP 5.0 in 2009, Microsoft Community Contributor(MCC) 2011.
 
Github : https://github.com/selvamselvam
Web site: http://www15.brinkster.com/selvamselvam
Follow on   LinkedIn

Comments and Discussions

 
GeneralMy vote of 5 Pinmemberpottabathhini krishna mohan25-Oct-13 1:52 
GeneralMy vote of 3 Pinmembervenkat.yva9-May-13 1:29 
QuestionNeed correction- PinmemberMember 939396730-Aug-12 3:19 
GeneralMy vote of 3 PinmemberShilpa from Mysore16-Aug-12 1:25 
QuestionGot an idea what a multithread is PinmemberMember 834154822-Oct-11 17:36 
Generaldon't understand to use of CCriticalSection Pinmembersteveh211217-Jun-11 0:24 
GeneralRe: don't understand to use of CCriticalSection PinmemberR.selvam19-Jun-11 5:31 
GeneralMy vote of 4 PinmemberHarishTheLearner15-May-11 22:28 
QuestionCRITICAL SECTION - Linker Error Pinmemberlgatcodeproject5-Jan-09 19:22 
AnswerRe: CRITICAL SECTION - Linker Error PinmemberR.selvam9-Sep-09 18:26 
GeneralThis is a great example PinmemberNed Bakelman20-Aug-08 11:12 
GeneralRe: This is a great example PinmemberR.selvam9-Sep-09 18:26 
QuestionHow Critical Section faster than Mutex? Pinmemberrajksingh27-Nov-07 20:58 
AnswerRe: How Critical Section faster than Mutex? PinmemberR.selvam9-Sep-09 18:22 
GeneralMFC Critical Section object Pinmembergaotter2-Jul-07 23:42 
GeneralLink to free online book PinmemberTopgun2k3-Jun-07 7:22 
GeneralRe: Link to free online book PinmemberR.selvam9-Sep-09 18:24 
QuestionProblem with InitializeCriticalSectionAndSpinCount Pinmembereryreyeryeryeryeyryye22-Mar-07 1:43 
AnswerRe: Problem with InitializeCriticalSectionAndSpinCount Pinmembervalterot27-Apr-07 7:43 
GeneralRe: Problem with InitializeCriticalSectionAndSpinCount PinmemberAjitsurana22-Jul-09 0:10 
GeneralLink to a similar article PinmemberNick Alexeev26-Dec-06 8:53 
GeneralGood Article Pinmemberajakimov17-Dec-06 4:46 
GeneralLock is not working!! PinmemberChrisRibe28-Sep-06 5:54 
GeneralRe: Lock is not working!! PinmemberChrisRibe29-Nov-06 12:36 
GeneralMutex Question PinmemberDavidLoeb8-Jun-06 11:30 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web04 | 2.8.141220.1 | Last Updated 12 Aug 2004
Article Copyright 2004 by R.selvam
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid