Á¤¸®> Thread Synchronization in User Mode
 FrontPage|FindPage|TitleIndex|RecentChanges|UserPreferences U E D R S P I M H RSS

Á¤¸®/°øºÎ¸Þ¸ðÀåÁ¤¸®/KernelObjectsÁ¤¸®/ThreadBasicsÁ¤¸®/ThreadPoolingÁ¤¸®/ThreadSchedulingAndPrioritiesAndAffinities › Á¤¸®/ThreadSynchronizationInUserMode
<!> UnderConstruction

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 );

3 Cache Lines #


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)

5 Critical Sections #


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

EditText|FindPage|DeletePage|LikePages| Valid XHTML 1.0! Valid CSS! powered by MoniWiki