Windows Thread Synchronization 41

 

 

 

 

 

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.

 

Synchronization with Interlocked Exchange Program Example: Creating new Win32 C++ console application project using Visual C++ .NET

 

Then, add the source file and give it a suitable name.

 

Synchronization with Interlocked Exchange Program Example: Adding new C++ source  file

 

Next, add the following source code.

 

#include <Windows.h>

#include <stdio.h>

 

// Shared global variables

LONG new_value = 1;

WCHAR message[] = L"Hello I'm a Thread";

 

DWORD WINAPI thread_function(PVOID arg)

{

      int count2;

     

      wprintf(L"thread_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(L"The process ID is %u\n", GetCurrentProcessId());

      wprintf(L"The 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(L"CreateThread() - Thread creation failed, error %u\n", GetLastError());

            exit(EXIT_FAILURE);

      }

      else

            wprintf(L"CreateThread() 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(L"Thread join failed! Error %u", GetLastError());

            exit(EXIT_FAILURE);

      }

      else

            wprintf(L"WaitForSingleObject() is OK, an object was signalled...\n");

     

      // Retrieve the code returned by the thread.

      if(GetExitCodeThread(a_thread, &thread_result) != 0)

            wprintf(L"GetExitCodeThread() is OK! Thread joined, exit code %u\n", thread_result);

      else

            wprintf(L"GetExitCodeThread() failed, error %u\n", GetLastError());

 

      if(CloseHandle(a_thread) != 0)

            wprintf(L"a_thread handle was closed successfully!\n");

      else

            wprintf(L"Failed to close a_thread handle, error %u\n", GetLastError());

 

      exit(EXIT_SUCCESS);

}

 

Build and run the project. The following screenshot is a sample output.

 

Synchronization with Interlocked Exchange Program Example: A sample console program output

 

Some Notes for IA64

 

  1. The InterlockedExchange() function generates a full memory barrier (or fence) and performs the exchange operation. This ensures the strict memory access ordering that is necessary, but it can decrease performance. To operate on 64-bit memory locations and values, use the InterlockedExchange64() function.

 

LONGLONG InterlockedExchange64(LONGLONG volatile* Target, LONGLONG Value);

 

  1. The variable pointed to by the Target parameter must be aligned on a 64-bit boundary.
  2. Be cautious about the variable pointed to by the Target parameter and the Value parameter; they must be of the data type longlong.

 

 

 

 

< Thread Synchronization 40 | Thread Synchronization Programming | Win32 Programming | Thread Synchronization 42 >