Synchronization with Interlocked Exchange Program Example
A simple form of synchronization is to use what is known as an interlocked exchange. An interlocked exchange performs a single operation that cannot be preempted. The functions InterlockedExchange(), InterlockedCompareExchange(), InterlockedDecrement(), InterlockedExchangeAdd(), and InterlockedIncrement() provide a simple mechanism for synchronizing access to a variable that is shared by multiple threads. The threads of different processes can use this mechanism if the variable is in shared memory. The InterlockedExchange() function atomically exchanges a pair of values. The function prevents more than one thread from using the same variable simultaneously. The variable pointed to by the target parameter must be aligned on a 32-bit boundary. These functions will fail on multiprocessor x86 systems and any non-x86 systems. Because this is not the case in the example, the example has limited value; but it does illustrate the use of the InterlockedExchange() functions. The InterlockedExchange() function should not be used on memory allocated with the PAGE_NOCACHE modifier. The following example demonstrates the usage of InterlockedExchange() for synchronizing the shared resource or global variable.
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
LONG new_value = 1;
WCHAR message[] = LHello I'm a Thread;
DWORD WINAPI thread_function(PVOID arg)
{
int count2;
wprintf(Lthread_function() is running. Argument sent was: %s\n, arg);
wprintf(L\nEntering child loop...\n);
for (count2 = 0; count2 < 10; count2++)
{
Sleep(1000);
wprintf(L (Y-%d), new_value);
// Sets a 32-bit variable to the specified value as an atomic operation.
// The function returns the initial value of the Target, parameter 1.
InterlockedExchange(&new_value, 1);
}
Sleep(3000);
return 0;
}
void wmain()
{
HANDLE a_thread;
DWORD a_threadId;
DWORD thread_result;
int count1;
wprintf(LThe process ID is %u\n, GetCurrentProcessId());
wprintf(LThe main() thread ID is %u\n, GetCurrentThreadId());
wprintf(L\n);
// Create a new thread.
a_thread = CreateThread(NULL, 0, thread_function, (PVOID)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(L\nEntering main() loop...\n);
for (count1 = 0; count1 < 10; count1++)
{
// Give ample time...
Sleep(1000);
wprintf(L(X-%d), new_value);
// Sets a 32-bit variable to the specified value as an atomic operation.
// The function returns the initial value of the Target, parameter 1.
InterlockedExchange(&new_value, 2);
}
wprintf(L\n\nWaiting for thread %u to finish...\n, a_threadId);
if (WaitForSingleObject(a_thread, INFINITE) != WAIT_OBJECT_0)
{
wprintf(LThread join failed! Error %u, GetLastError());
exit(EXIT_FAILURE);
}
else
wprintf(LWaitForSingleObject() is OK, an object was signalled...\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());
exit(EXIT_SUCCESS);
}
Build and run the project. The following screenshot is a sample output.
Some Notes for IA64
LONGLONG InterlockedExchange64(LONGLONG volatile* Target, LONGLONG Value);