Synchronization with Critical Sections Program Example
Another mechanism for solving this simple scenario is to use a critical section. A critical section is similar to InterlockedExchange except that you have the ability to define the logic that takes place as an atomic operation. Critical section objects provide synchronization similar to that provided by mutex objects, except that critical section objects can be used only by the threads of a single process. Critical section objects provide a slightly faster, more efficient mechanism for mutual-exclusion synchronization (a processor-specific test and set instruction) as compared to event, mutex, and semaphore objects, which can also be used in a single-process application. There is no guarantee about the order in which threads will obtain ownership of the critical section; however, the system will be fair to all threads. Unlike a mutex object, there is no way to tell whether a critical section has been abandoned. The process is responsible for allocating the memory used by a critical section. Typically, this is done by just declaring a variable of type CRITICAL_SECTION. Before the threads of the process can use it, initialize the critical section and then request ownership of a critical section. If the critical section object is currently owned by another thread, the process waits indefinitely for ownership. In contrast, when a mutex object is used for mutual exclusion, the wait functions accept a specified time-out interval. The TryEnterCriticalSection function attempts to enter a critical section without blocking the calling thread.
A thread uses the InitializeCriticalSectionAndSpinCount or SetCriticalSectionSpinCount functions to specify a spin count for the critical section object. On single-processor systems, the spin count is ignored and the critical section spin count is set to 0. On multiprocessor systems, if the critical section is unavailable, the calling thread will spin dwSpinCount times before performing a wait operation on a semaphore associated with the critical section. If the critical section becomes free during the spin operation, the calling thread avoids the wait operation. Any thread of the process can release the system resources that were allocated when the critical section object was initialized. After this function has been called, the critical section object can no longer be used for synchronization.
Create a new empty Win32 console application project. Give a suitable project name and change the project location if needed.
Then, add the source file and give it a suitable name.
Next, add the following source code.
#include <Windows.h>
#include <stdio.h>
// Shared global variables
CRITICAL_SECTION g_cs;
WCHAR message[] = LHello I'm new Thread;
DWORD WINAPI thread_function(LPVOID arg)
{
int count2;
wprintf(Lthread_function() is running. Argument received was: %s\n, arg);
wprintf(LChild thread entering critical section, doing job & leaving critical section...\n);
wprintf(L\n);
for (count2 = 0; count2 < 10; count2++)
{
// Waits for ownership of the specified critical section object.
// The function returns when the calling thread is granted ownership.
// This function does not return a value.
EnterCriticalSection(&g_cs);
wprintf(LY );
// Releases ownership of the specified critical section object.
// This function does not return a value.
LeaveCriticalSection(&g_cs);
}
Sleep(4000);
return 0;
}
int wmain()
{
HANDLE a_thread;
DWORD a_threadId;
DWORD thread_result;
int count1;
// Some info
wprintf(LThe current process ID is %u\n, GetCurrentProcessId());
wprintf(LThe main thread ID is %u\n, GetCurrentThreadId());
wprintf(L\n);
// Initializes a critical section object.
// This function does not return a value.
InitializeCriticalSection(&g_cs);
// Create a new thread.
a_thread = CreateThread(NULL, 0, thread_function, (LPVOID)message, 0, &a_threadId);
if (a_thread == NULL)
{
wprintf(LCreateThread() - Thread creation failed, error %u\n, GetLastError());
exit(EXIT_FAILURE);
}
else
wprintf(LCreateThread() is OK, thread ID is %u\n, a_threadId);
wprintf(Lmain() thread entering critical section, doing job & leaving critical section...\n);
wprintf(LEntering main() loop...\n);
for (count1 = 0; count1 < 10; count1++)
{
EnterCriticalSection(&g_cs);
wprintf(L X);
LeaveCriticalSection(&g_cs);
}
Sleep(3000);
wprintf(L\n\nWaiting for thread %u to finish...\n, a_threadId);
if (WaitForSingleObject(a_thread, INFINITE) != WAIT_OBJECT_0)
{
wprintf(LWaitForSingleObject() - Thread join failed, error %u, GetLastError());
exit(EXIT_FAILURE);
}
else
wprintf(LWaitForSingleObject() is OK! An object was signaled\n);
// Retrieve the code returned by the thread.
if(GetExitCodeThread(a_thread, &thread_result) != 0)
wprintf(LGetExitCodeThread() is OK! Thread joined, exit code %u\n, thread_result);
else
wprintf(LGetExitCodeThread() failed, error %u\n, GetLastError());
if(CloseHandle(a_thread) != 0)
wprintf(La_thread handle was closed successfully!\n);
else
wprintf(LFailed to close a_thread handle, error %u\n, GetLastError());
// Releases all resources used by an unowned critical section object.
// This function does not return a value.
wprintf(LDeleting critical section resources...\n);
DeleteCriticalSection(&g_cs);
exit(EXIT_SUCCESS);
}
Build and run the project. The following are the sample outputs when the program was run many times.