Contents:
- 1 Thread Synchronization in User Mode
- 2 Atomic Access: The Interlocked Family of Functions
1 Thread Synchronization in User Mode #
2 Atomic Access: The Interlocked Family of Functions #
// Define a global variable.
long g_x = 0;
DWORD WINAPI ThreadFunc1( PVOID pvParam )
{
g_x++;
return ( 0 );
}
DWORD WINAPI ThreadFunc2( PVOID pvParam )
{
g_x++;
return ( 0 );
}
MOV EAX, [g_x] ; Move the value in g_x into a register.
INC EAX ; Increment the value in the register.
MOV [g_x], EAX ; Store the new value back in g_x.
MOV EAX, [g_x] ; Thread 1: Move 0 into a register.
INC EAX ; Thread 1: Increment the register to 1.
MOV [g_x], EAX ; Thread 1: Store 1 back in g_x.
MOV EAX, [g_x] ; Thread 2: Move 1 into a register.
INC EAX ; Thread 2: Increment the register to 2.
MOV [g_x], EAX ; Thread 2: Store 2 back in g_x.
MOV EAX, [g_x] ; Thread 1: Move 0 into a register.
INC EAX ; Thread 1: Increment the register to 1.
MOV EAX, [g_x] ; Thread 2: Move 0 into a register.
INC EAX ; Thread 2: Increment the register to 1.
MOV [g_x], EAX ; Thread 2: Store 1 back in g_x.
MOV [g_x], EAX ; Thread 1: Store 1 back in g_x.
LONG InterlockedExchangeAdd(
PLONG plAddend,
LONG lIncrement
);
// Define a global variable.
long g_x = 0;
DWORD WINAPI ThreadFunc1( PVOID pvParam )
{
InterlockedExchangeAdd( &g_x, 1 );
return ( 0 );
}
DWORD WINAPI ThreadFunc2( PVOID pvParam )
{
InterlockedExchangeAdd( &g_x, 1 );
return ( 0 );
}
// The long variable shared by many threads
LONG g_x;
...
// Incorrect way to increment the long
g_x++;
...
// Correct way to increment the long
InterlockedExchangeAdd( &g_x, 1 );
LONG InterlockedExchange(
PLONG plTarget,
LONG lValue
);
PVOID InterlockedExchangePointer(
PVOID* ppvTarget,
PVOID pvValue
);
// Global variable indicating whether a shared resource is in use or not
BOOL g_fResourceInUse = FALSE;
...
void Func1()
{
// Wait to access the resource.
while( InterlockedExchange( &g_fResourceInUse, TRUE ) == TRUE )
Sleep( 0 );
// Access the resource.
...
// We no longer need to access the resource.
InterlockedExchange( &g_fResourceInUse, FALSE );
}
PVOID InterlockedCompareExchange(
PLONG plDestination,
LONG lExchange,
LONG lComparand
);
PVOID InterlockedCompareExchangePointer(
PVOID* ppvDestination,
PVOID pvExchange,
PVOID pvComparand
);
LONG InterlockedCompareExchange( PLONG plDestination,
LONG lExchange, LONG lComparand )
{
LONG lRet = *plDestination; // Original value
if( *plDestination == lComparand )
*plDestination = lExchange;
return ( lRet );
}
LONG InterlockedIncrement( PLONG plAddend );
LONG InterlockedDecrement( PLONG plAddend );
struct CUSTINFO {
DWORD dwCustomerID; // Mostly read-only
int nBalanceDue; // Read-write
char szName[100]; // Mostly read-only
FILETIME ftLastOrderDate; // Read-write
};
// Determine the cache line size for the host CPU.
#ifdef _X86_
#define CACHE_ALIGN 32
#endif
#ifdef _ALPHA_
#define CACHE_ALIGN 64
#endif
#ifdef _IA64_
#define CACHE_ALIGN ??
#endif
#define CACHE_PAD( Name, BytesSoFar ) \
BYTE Name[ CACHE_ALIGN - ( (BytesSoFar) % CACHE_ALIGN ) ]
struct CUSTINFO {
DWORD dwCustomerID; // Mostly read-only
char szName[100]; // Mostly read-only
// Force the following members to be in a different cache line.
CACHE_PAD( bPad1, sizeof( DWORD ) + 100 );
int nBalanceDue; // Read-write
FILETIME ftLastOrderDate; // Read-write
// Force the following structure to be in a different cache line.
CACHE_PAD( bPad2, sizeof( int ) + sizeof( FILETIME ) );
};
4 Advanced Thread Synchronization #
4.1 A Technique to Avoid #
volatile BOOL g_fFinishedCalculation = FALSE;
int WINAPI WinMain( ... )
{
CreateThread( ..., RecalcFunc, ... );
...
// Wait for the recalculation to complete.
while( !g_fFinishedCalculation )
;
...
}
DWORD WINAPI RecalcFunc( PVOID pvParam )
{
// Perform the recalculation.
...
g_fFinishedCalculation = TRUE;
return ( 0 );
}
MOV Reg0, [g_fFinishedCalculation] ; Copy the value into a register
Label: TEST Reg0, 0 ; Is the value 0
JMP Reg0 == 0, Label ; The register is 0, try again
... ; The register is not 0 (end of loop)
const int MAX_TIMES = 1000;
int g_nIndex = 0;
DWORD g_dwTimes[ MAX_TIMES ];
DWORD WINAPI FirstThread( PVOID pvParam )
{
while( g_nIndex < MAX_TIMES )
{
g_dwTimes[ g_nIndex ] = GetTickCount();
g_nIndex++;
}
return ( 0 );
}
DWORD WINAPI SecondThread( PVOID pvParam )
{
while( g_nIndex < MAX_TIMES )
{
g_nIndex++;
g_dwTimes[ g_nIndex - 1 ] = GetTickCount();
}
return ( 0 );
}
const int MAX_TIMES = 1000;
int g_nIndex = 0;
DWORD g_dwTimes[ MAX_TIMES ];
CRITICAL_SECTION g_cs;
DWORD WINAPI FirstThread( PVOID pvParam )
{
while( g_nIndex < MAX_TIMES )
{
EnterCriticalSection( &g_cs );
g_dwTimes[ g_nIndex ] = GetTickCount();
g_nIndex++;
LeaveCriticalSection( &g_cs );
}
return ( 0 );
}
DWORD WINAPI SecondThread( PVOID pvParam )
{
while( g_nIndex < MAX_TIMES )
{
EnterCriticalSection( &g_cs );
g_nIndex++;
g_dwTimes[ g_nIndex - 1 ] = GetTickCount();
LeaveCriticalSection( &g_cs );
}
return ( 0 );
}
5.1 Critical Sections:The Fine Print #
VOID InitializeCriticalSection( PCRITICAL_SECTION pcs );
VOID DeleteCriticalSection( PCRITICAL_SECTION pcs );
VOID EnterCriticalSection( PCRITICAL_SECTION pcs );
HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager
BOOL TryEnterCriticalSection( PCRITICAL_SECTION pcs );
VOID LeaveCriticalSection( PCRITICAL_SECTION pcs );
5.2 Critical Sections and Spinlocks #
BOOL InitializeCriticalSectionAndSpinCount(
PCRITICAL_SECTION pcs,
DWORD dwSpinCount
);
DWORD SetCriticalSectionSpinCount(
PCRITICAL_SECTION pcs,
DWORD dwSpinCount
);
5.3 Critical Sections and Error Handling #
5.4 Useful Tips and Techniques #
5.5 Use One CRITICAL_SECTION Variable per Shared Resource #
int g_nNums[ 100 ]; // A shared resource
TCHAR g_cChars[ 100 ]; // Another shared resource
CRITICAL_SECTION g_cs; // Guards both resources
DWORD WINAPI ThreadFunc( PVOID pvParam )
{
EnterCriticalSection( &g_cs );
for( int x = 0; x < 100; x++ )
{
g_nNums[ x ] = 0;
g_cChars[ x ] = TEXT( 'X' );
}
LeaveCriticalSection( &g_cs );
return ( 0 );
}
DWORD WINAPI ThreadFunc( PVOID pvParam )
{
EnterCriticalSection( &g_cs );
for( int x = 0; x < 100; x++ )
g_nNums[ x ] = 0;
for( x = 0; x < 100; x++ )
g_cChars[x] = TEXT( 'X' );
LeaveCriticalSection( &g_cs );
return ( 0 );
}
int g_nNum[ 100 ]; // A shared resource
CRITICAL_SECTION g_csNums; // Guards g_nNums
TCHAR g_cChars[ 100 ]; // Another shared resource
CRITICAL_SECTION g_csChars; // Guards g_cChars
DWORD WINAPI ThreadFunc( PVOID pvParam )
{
EnterCriticalSection( &g_csNums );
for( int x = 0; x < 100; x++ )
g_nNums[ x ] = 0;
LeaveCriticalSection( &g_csNums );
EnterCriticalSection( &g_csChars );
for( x = 0; x < 100; x++ )
g_cChars[ x ] = TEXT( 'X' );
LeaveCriticalSection( &g_ csChars );
return ( 0 );
}
5.6 Access Multiple Resources Simultaneously #
DWORD WINAPI ThreadFunc( PVOID pvParam )
{
EnterCriticalSection( &g_csNums );
EnterCriticalSection( &g_csChars );
// This loop requires simultaneous access to both resources.
for( int x = 0; x < 100; x++ )
g_nNums[ x ] = g_cChars[ x ];
LeaveCriticalSection( &g_csChars );
LeaveCriticalSection( &g_csNums );
return ( 0 );
}
DWORD WINAPI OtherThreadFunc( PVOID pvParam )
{
EnterCriticalSection( &g_csChars );
EnterCriticalSection( &g_csNums );
for( int x = 0; x < 100; x++ )
g_nNums[ x ] = g_cChars[ x ];
LeaveCriticalSection( &g_csNums );
LeaveCriticalSection( &g_csChars );
return ( 0 );
}
5.7 Don't Hold Critical Sections for a Long Time #
SOMESTRUCT g_s;
CRITICAL_SECTION g_cs;
DWORD WINAPI SomeThread( PVOID pvParam )
{
EnterCriticalSection( &g_cs );
// Send a message to a window.
SendMessage( hwndSomeWnd, WM_SOMEMSG, &g_s, 0 );
LeaveCriticalSection( &g_cs );
return ( 0 );
}
SOMESTRUCT g_s;
CRITICAL_SECTION g_cs;
DWORD WINAPI SomeThread( PVOID pvParam )
{
EnterCriticalSection( &g_cs );
SOMESTRUCT sTemp = g_s;
LeaveCriticalSection( &g_cs );
// Send a message to a window.
SendMessage( hwndSomeWnd, WM_SOMEMSG, &sTemp, 0 );
return ( 0 );
}
CategoryTranslation