Atomic
Updates of Variables
The Windows API provides a large number of atomic operations, which are
referred to in Windows terminology as interlocked
functions. For the full list of functions, refer to the Windows
documentation. An example of one such function is InterlockedExchangeAdd(), which atomically adds a value to a variable of type
LONG (in Windows a long variable is 32 bits in size, and
a LONGLONG is 64
bits in size; the size does not change depending on whether the application is
32-bit or 64-bit). Listing 6.36 shows an example of using InterlockedExchangeAdd() to atomically increment a variable by ten.
Listing 6.36 Example
of Using InterlockedAdd()
#include
<windows.h>
void
update( LONG* value )
{
InterlockedExchangeAdd(
value, 10 );
}
The range of atomic operations available
encompasses and, or, xor, add, increment,
and decrement. All the functions are
available for long and longlong data types; some of them are
also available for smaller data types like char.
There are also some functions that modify the
variable and return the old value. InterlockedCompareExchange()
performs a compare and swap operation where if
the value of the variable matches the expected
value, the value of the variable is exchanged with a new value. InterlockedBitTestAndSet() returns the value of a specified bit in the variable and sets its new
value to one. Similarly, InterlockedBitTestAndReset() provides the same return value but sets the new
value to zero.
The code in Listing 6.37 creates two threads and
uses InterlockedIncrement() to increment the variable counter shared between the two threads. This approach is lower latency than
using a mutex or some other synchronization mechanism.
Listing 6.37 Using
Atomic Operations to Protect a Shared Variable
#include <math.h> #include <stdio.h>
#include <windows.h> #include <process.h>
int
isprime( int number )
{
int i;
for (i = 2; i < (int)( sqrt( (float)number )+1.0 ); i++ )
{
if ( number%i == 0) { return 0; }
}
return 1;
}
volatile
long counter = 0;
unsigned
int __stdcall test( void * )
{
while ( counter<100 )
{
int number =
InterlockedIncrement( &counter );
printf( "ThreadID %i; value =
%i, is prime = %i\n", GetCurrentThreadId(), number, isprime(number) );
}
return 0;
}
int _tmain( int argc, _TCHAR* argv[] )
{
HANDLE h1, h2;
h1 = (HANDLE)_beginthreadex( 0, 0, &test,
(void*)0, 0, 0); h2 = (HANDLE)_beginthreadex( 0, 0, &test, (void*)0, 0, 0);
WaitForSingleObject( h1, INFINITE );
WaitForSingleObject( h2, INFINITE );
CloseHandle( h1 );
CloseHandle( h2 ); getchar();
return 0;
}
Most of these functions enforce full memory ordering, so all memory
operations prior to the call become visible to other processors before the
atomic operation com-pletes and any operations performed after the atomic
operation becomes visible to other processors as happening after the atomic
operation.
Related Topics
Privacy Policy, Terms and Conditions, DMCA Policy and Compliant
Copyright © 2018-2023 BrainKart.com; All Rights Reserved. Developed by Therithal info, Chennai.