沥府> Thread Synchronization With Kernel Objects
 FrontPage|FindPage|TitleIndex|RecentChanges|UserPreferences U E D R S P I M H RSS

沥府/ThreadBasics沥府/ThreadPooling沥府/ThreadSchedulingAndPrioritiesAndAffinities沥府/ThreadSynchronizationInUserMode沥府/ThreadSynchronizationToolkit › 沥府/ThreadSynchronizationWithKernelObjects
<!> UnderConstruction

Contents:
1 Thread Synchronization With Kernel Objects
2 Wait Functions


1 Thread Synchronization With Kernel Objects #


2 Wait Functions #


DWORD WaitForSingleObject(
	HANDLE hObject,
	DWORD dwMilliseconds
);

WaitForSingleObject( hProcess, INFINITE );

DWORD dw = WaitForSingleObject( hProcess, 5000 );
switch( dw )
{
    case WAIT_OBJECT_0:
        // The process terminated.
        break;

    case WAIT_TIMEOUT:
        // The process did not terminate within 5000 milliseconds.
        break;

    case WAIT_FAILED:
        // Bad call to function (invalid handle?)
        break;
}

DWORD WaitForMultipleObjects(
	DWORD dwCount,
	CONST HANDLE* phObjects,
	BOOL fWaitAll,
	DWORD dwMilliseconds
);

HANDLE h[ 3 ];
h[ 0 ] = hProcess1;
h[ 1 ] = hProcess2;
h[ 2 ] = hProcess3;
DWORD dw = WaitForMultipleObjects( 3, h, FALSE, 5000 );

switch( dw )
{
    case WAIT_FAILED:
        // Bad call to function (invalid handle?)
        break;

    case WAIT_TIMEOUT:
        // None of the objects became signaled within 5000 milliseconds.
        break;

    case WAIT_OBJECT_0 + 0:
        // The process identified by h[0] (hProcess1) terminated.
        break;

    case WAIT_OBJECT_0 + 1:
        // The process identified by h[1] (hProcess2) terminated.
        break;

    case WAIT_OBJECT_0 + 2:
        // The process identified by h[2] (hProcess3) terminated.
        break;
}

3 Successful Wait Side Effects #


HANDLE h[ 2 ];
h[ 0 ] = hAutoResetEvent1;		// Initially nonsignaled
h[ 1 ] = hAutoResetEvent2;		// Initially nonsignaled
WaitForMultipleObjects( 2, h, TRUE, INFINITE );

4 Event Kernel Objects #


HANDLE CreateEvent(
	PSECURITY_ATTRIBUTES psa,
	BOOL fManualReset,
	BOOL fInitialState,
	PCTSTR pszName
);

HANDLE OpenEvent(
	DWORD fdwAccess,
	BOOL fInherit,
	PCTSTR pszName
);

BOOL SetEvent( HANDLE hEvent );

BOOL ResetEvent( HANDLE hEvent );

// Create a global handle to a manual-reset, nonsignaled event.
HANDLE g_hEvent;

int WINAPI WinMain( ... )
{
    // Create the manual-reset, nonsignaled event.
    g_hEvent = CreateEve\nt( NULL, TRUE, FALSE, NULL );

    // Spawn 3 new threads.
    HANDLE hThread[ 3 ];
    DWORD dwThreadID;
    hThread[ 0 ] = _beginthreadex( NULL, 0, WordCount,    NULL, 0, &dwThreadID );
    hThread[ 1 ] = _beginthreadex( NULL, 0, SpellCheck,   NULL, 0, &dwThreadID );
    hThread[ 2 ] = _beginthreadex( NULL, 0, GrammarCheck, NULL, 0, &dwThreadID );


    OpenFileAndReadContentsIntoMemory( ... );

    // Allow all 3 threads to access the memory.
    SetEvent( g_hEvent );
    ...
}

DWORD WINAPI WordCount( PVOID pvParam )
{
    // Wait until the file's data is in memory.
    WaitForSingleObject( g_hEvent, INFINITE );

    // Access the memory block.
    ...
    return ( 0 );
}

DWORD WINAPI SpellCheck (PVOID pvParam)
{
    // Wait until the file's data is in memory.
    WaitForSingleObject( g_hEvent, INFINITE );

    // Access the memory block.
    ...
    return ( 0 );
}

DWORD WINAPI GrammarCheck (PVOID pvParam)
{
    // Wait until the file's data is in memory.
    WaitForSingleObject( g_hEvent, INFINITE );

    // Access the memory block.
    ...
    return ( 0 );
}

BOOL PulseEvent( HANDLE hEvent );

4.1 The Handshake Sample Application #


Handshake.cpp

/******************************************************************************
Module:  Handshake.cpp
Notices: Copyright (c) 2000 Jeffrey Richter
******************************************************************************/


#include "..\CmnHdr.h"      /* See Appendix A. */
#include <windowsx.h>
#include <tchar.h>
#include <process.h>        // For beginthreadex
#include "Resource.h"


///////////////////////////////////////////////////////////////////////////////


// This event is signaled when the client has a request for the server
HANDLE g_hevtRequestSubmitted;

// This event is signaled when the server has a result for the client
HANDLE g_hevtResultReturned;

// The buffer shared between the client and server threads
TCHAR  g_szSharedRequestAndResultBuffer[ 1024 ];

// The special value sent from the client that causes the
// server thread to terminate cleanly.
TCHAR  g_szServerShutdown[] = TEXT( "Server Shutdown" );


///////////////////////////////////////////////////////////////////////////////


// This is the code executed by the server thread
DWORD WINAPI ServerThread( PVOID pvParam )
{
    // Assume that the server thread is to run forever
    BOOL fShutdown = FALSE;

    while( !fShutdown )
    {
        // Wait for the client to submit a request
        WaitForSingleObject( g_hevtRequestSubmitted, INFINITE );

        // Check to see if the client wants the server to terminate
        fShutdown =
            ( lstrcmpi( g_szSharedRequestAndResultBuffer, g_szServerShutdown ) == 0 );

        if( !fShutdown )
        {
            // Process the client's request (reverse the string)
            _tcsrev( g_szSharedRequestAndResultBuffer );
        }

        // Let the client process the request's result
        SetEvent( g_hevtResultReturned );
    }

    // The client wants us to shutdown, exit
    return ( 0 );
}


///////////////////////////////////////////////////////////////////////////////


BOOL Dlg_OnInitDialog( HWND hwnd, HWND hwndFocus, LPARAM lParam )
{
    chSETDLGICONS( hwnd, IDI_HANDSHAKE );

    // Initialize the edit control with some test data request
    Edit_SetText( GetDlgItem(hwnd, IDC_REQUEST ), TEXT( "Some test data" ) );

    return ( TRUE );
}


///////////////////////////////////////////////////////////////////////////////


void Dlg_OnCommand( HWND hwnd, int id, HWND hwndCtl, UINT codeNotify )
{
    switch( id )
    {
        case IDCANCEL:
            EndDialog( hwnd, id );
            break;

        case IDC_SUBMIT:    // Submit a request to the server thread

            // Copy the request string into the shared data buffer
            Edit_GetText( GetDlgItem( hwnd, IDC_REQUEST ),
                g_szSharedRequestAndResultBuffer,
                chDIMOF( g_szSharedRequestAndResultBuffer ) );

            // Let the server thread know that a request is ready in the buffer
            SetEvent( g_hevtRequestSubmitted );

            // Wait for the server to process the request and give us the result
            WaitForSingleObject( g_hevtResultReturned, INFINITE );

            // Let the user know the result
            Edit_SetText( GetDlgItem( hwnd, IDC_RESULT ),
                g_szSharedRequestAndResultBuffer );

            break;
    }
}


///////////////////////////////////////////////////////////////////////////////


INT_PTR WINAPI Dlg_Proc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) {

    switch( uMsg )
    {
        chHANDLE_DLGMSG( hwnd, WM_INITDIALOG, Dlg_OnInitDialog );
        chHANDLE_DLGMSG( hwnd, WM_COMMAND,    Dlg_OnCommand );
    }

    return ( FALSE );
}


///////////////////////////////////////////////////////////////////////////////


int WINAPI _tWinMain( HINSTANCE hinstExe, HINSTANCE, PTSTR pszCmdLine, int )
{
    // Create & initialize the 2 nonsignaled, auto-reset events
    g_hevtRequestSubmitted = CreateEvent( NULL, FALSE, FALSE, NULL );
    g_hevtResultReturned   = CreateEvent( NULL, FALSE, FALSE, NULL );

    // Spawn the server thread
    DWORD dwThreadID;
    HANDLE hThreadServer = chBEGINTHREADEX( NULL, 0, ServerThread, NULL,
        0, &dwThreadID );

    // Execute the client thread's user-interface
    DialogBox( hinstExe, MAKEINTRESOURCE( IDD_HANDSHAKE ), NULL, Dlg_Proc );

    // The client's UI is closing, have the server thread shutdown
    lstrcpy( g_szSharedRequestAndResultBuffer, g_szServerShutdown );
    SetEvent( g_hevtRequestSubmitted );

    // Wait for the server thread to acknowledge the shutdown AND
    // wait for the server thread to fully terminate
    HANDLE h[ 2 ];
    h[ 0 ] = g_hevtResultReturned;
    h[ 1 ] = hThreadServer;
    WaitForMultipleObjects( 2, h, TRUE, INFINITE );

    // Properly clean up everything
    CloseHandle( hThreadServer );
    CloseHandle( g_hevtRequestSubmitted );
    CloseHandle( g_hevtResultReturned );

    // The client thread terminates with the whole process
    return ( 0 );
}


//////////////////////////////// End of File //////////////////////////////////

Handshake.rc

// Microsoft Developer Studio generated resource script.
//
#include "resource.h"

#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "afxres.h"

/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS

/////////////////////////////////////////////////////////////////////////////
// English (U.S.) resources

#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
#ifdef _WIN32
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
#pragma code_page(1252)
#endif //_WIN32

/////////////////////////////////////////////////////////////////////////////
//
// Dialog
//

IDD_HANDSHAKE DIALOG DISCARDABLE  0, 0, 256, 81
STYLE DS_CENTER | WS_MINIMIZEBOX | WS_CAPTION | WS_SYSMENU
CAPTION "Handshake"
FONT 8, "MS Sans Serif"
BEGIN
    GROUPBOX        "Client side",IDC_STATIC,4,4,248,72
    LTEXT           "&Request:",IDC_STATIC,12,18,30,8
    EDITTEXT        IDC_REQUEST,48,16,196,14,ES_AUTOHSCROLL
    DEFPUSHBUTTON   "&Submit Request to Server",IDC_SUBMIT,80,36,96,14
    LTEXT           "Result:",IDC_STATIC,12,58,23,8
    EDITTEXT        IDC_RESULT,48,56,196,16,ES_AUTOHSCROLL | ES_READONLY
END


/////////////////////////////////////////////////////////////////////////////
//
// DESIGNINFO
//

#ifdef APSTUDIO_INVOKED
GUIDELINES DESIGNINFO DISCARDABLE
BEGIN
    IDD_HANDSHAKE, DIALOG
    BEGIN
        LEFTMARGIN, 7
        RIGHTMARGIN, 249
        TOPMARGIN, 7
        BOTTOMMARGIN, 74
    END
END
#endif  // APSTUDIO_INVOKED


#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//

1 TEXTINCLUDE DISCARDABLE
BEGIN
    "resource.h\0"
END

2 TEXTINCLUDE DISCARDABLE
BEGIN
    "#include ""afxres.h""\r\n"
    "\0"
END

3 TEXTINCLUDE DISCARDABLE
BEGIN
    "\r\n"
    "\0"
END

#endif  // APSTUDIO_INVOKED


/////////////////////////////////////////////////////////////////////////////
//
// Icon
//

// Icon with lowest ID value placed first to ensure application icon
// remains consistent on all systems.
IDI_HANDSHAKE           ICON    DISCARDABLE     "Handshake.ico"
#endif  // English (U.S.) resources
/////////////////////////////////////////////////////////////////////////////



#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//


/////////////////////////////////////////////////////////////////////////////
#endif  // not APSTUDIO_INVOKED

5 Waitable Timer Kernel Objects #


HANDLE CreateWaitableTimer(
	PSECURITY_ATTRIBUTES psa,
	BOOL fManualReset,
	PCTSTR pszName
);

HANDLE OpenWaitableTimer(
	DWORD dwDesiredAccess,
	BOOL bInheritHandle,
	PCTSTR pszName
);

BOOL SetWaitableTimer(
	HANDLE hTimer,
	const LARGE_INTEGER *pDueTime,
	LONG lPeriod,
	PTIMERAPCROUTINE pfnCompletionRoutine,
	PVOID pvArgToCompletionRoutine,
	BOOL fResume
);

// Declare our local variables.
HANDLE hTimer;
SYSTEMTIME st;
FILETIME ftLocal, ftUTC;
LARGE_INTEGER liUTC;

// Create an auto-reset timer.
hTimer = CreateWaitableTimer( NULL, FALSE, NULL );

// First signaling is at January 1, 2002, at 1:00 P.M. (local time).
st.wYear         = 2002;    // Year
st.wMonth        = 1;       // January
st.wDayOfWeek    = 0;       // Ignored
st.wDay          = 1;       // The first of the month
st.wHour         = 13;      // 1PM
st.wMinute       = 0;       // 0 minutes into the hour
st.wSecond       = 0;       // 0 seconds into the minute
st.wMilliseconds = 0;       // 0 milliseconds into the second

SystemTimeToFileTime( &st, &ftLocal );

// Convert local time to UTC time.
LocalFileTimeToFileTime( &ftLocal, &ftUTC );
// Convert FILETIME to LARGE_INTEGER because of different alignment.
liUTC.LowPart  = ftUTC.dwLowDateTime;
liUTC.HighPart = ftUTC.dwHighDateTime;

// Set the timer.
SetWaitableTimer( hTimer, &liUTC, 6 * 60 * 60 * 1000,
    NULL, NULL, FALSE );

...

// Set the timer.
SetWaitableTimer( hTimer, (PLARGE_INTEGER) &ftUTC,
	6 * 60 * 60 * 1000, NULL, NULL, FALSE );

// Declare our local variables.
HANDLE hTimer;
LARGE_INTEGER li;

// Create an auto-reset timer.
hTimer = CreateWaitableTimer( NULL, FALSE, NULL );

// Set the timer to go off 5 seconds after calling SetWaitableTimer.
// Timer unit is 100-nanoseconds.
const int nTimerUnitsPerSecond = 10000000;

// Negate the time so that SetWaitableTimer knows we
// want relative time instead of absolute time.
li.QuadPart = -( 5 * nTimerUnitsPerSecond );

// Set the timer.
SetWaitableTimer( hTimer, &li, 6 * 60 * 60 * 1000,
    NULL, NULL, FALSE );

...

BOOL CancelWaitableTimer( HANDLE hTimer );

5.1 Having Waitable Timers Queue APC Entries #


VOID APIENTRY TimerAPCRoutine( PVOID pvArgToCompletionRoutine,
	DWORD dwTimerLowValue, DWORD dwTimerHighValue )
{
	// Do whatever you want here.
}

VOID APIENTRY TimerAPCRoutine( PVOID pvArgToCompletionRoutine,
    DWORD dwTimerLowValue, DWORD dwTimerHighValue )
{
    FILETIME ftUTC, ftLocal;
    SYSTEMTIME st;
    TCHAR szBuf[ 256 ];

    // Put the time in a FILETIME structure.
    ftUTC.dwLowDateTime  = dwTimerLowValue;
    ftUTC.dwHighDateTime = dwTimerHighValue;

    // Convert the UTC time to the user's local time.
    FileTimeToLocalFileTime( &ftUTC, &ftLocal );

    // Convert the FILETIME to the SYSTEMTIME structure
    // required by GetDateFormat and GetTimeFormat.
    FileTimeToSystemTime( &ftLocal, &st );

    // Construct a string with the
    // date/time that the timer went off.
    GetDateFormat( LOCALE_USER_DEFAULT, DATE_LONGDATE,
        &st, NULL, szBuf, sizeof( szBuf ) / sizeof( TCHAR ) );
    _tcscat( szBuf, _ _TEXT( " " ) );
    GetTimeFormat( LOCALE_USER_DEFAULT, 0,
        &st, NULL, _tcschr( szBuf, 0 ),
        sizeof( szBuf ) / sizeof( TCHAR ) - _tcslen( szBuf ) );

    // Show the time to the user.
    MessageBox( NULL, szBuf, "Timer went off at...", MB_OK );
}

void SomeFunc()
{
    // Create a timer. (It doesn't matter whether it's manual-reset
    // or auto-reset.)
    HANDLE hTimer = CreateWaitableTimer( NULL, TRUE, NULL );

    // Set timer to go off in 5 seconds.
    LARGE_INTEGER li = { 0 };
    SetWaitableTimer( hTimer, &li, 5000, TimerAPCRoutine, NULL, FALSE );

    // Wait in an alertable state for the timer to go off.
    SleepEx( INFINITE, TRUE );

    CloseHandle( hTimer );
}

HANDLE hTimer = CreateWaitableTimer( NULL, FALSE, NULL );
SetWaitableTimer( hTimer, ..., TimerAPCRoutine, ... );
WaitForSingleObjectEx( hTimer, INFINITE, TRUE );

5.2 Timer Loose Ends #


6 Semaphore Kernel Objects #


HANDLE CreateSemaphore(
	PSECURITY_ATTRIBUTE psa,
	LONG lInitialCount,
	LONG lMaximumCount,
	PCTSTR pszName
);

HANDLE OpenSemaphore(
	DWORD fdwAccess,
	BOOL bInheritHandle,
	PCTSTR pszName
);

HANDLE hsem = CreateSemaphore( NULL, 0, 5, NULL );

BOOL ReleaseSemaphore(
	HANDLE hsem,
	LONG lReleaseCount,
	PLONG plPreviousCount
);

7 Mutex Kernel Objects #


HANDLE CreateMutex(
	PSECURITY_ATTRIBUTES psa,
	BOOL fInitialOwner,
	PCTSTR pszName
);

HANDLE OpenMutex(
	DWORD fdwAccess,
	BOOL bInheritHandle,
	PCTSTR pszName
);

BOOL ReleaseMutex( HANDLE hMutex );

7.1 Abandonment Issues #



7.2 Mutexes vs. Critical Sections #


CharacteristicMutexCritical Section
Performance Slow Fast
Can be used across process boundaries Yes No
Declaration HANDLE hmtx; CRITICAL_SECTION cs;
Initialization hmtx= CreateMutex (NULL, FALSE, NULL); InitializeCriticalSection(&cs);
Cleanup CloseHandle(hmtx); ''DeleteCriticalSection(&cs);'
Infinite wait WaitForSingleObject (hmtx, INFINITE); EnterCriticalSection(&cs);
0 wait WaitForSingleObject (hmtx, 0); TryEnterCriticalSection(&cs);
Arbitrary wait WaitForSingleObject (hmtx, dwMilliseconds); Not possible
Release ReleaseMutex(hmtx); LeaveCriticalSection(&cs);
Can be waited on with other kernel objects Yes (use WaitForMultipleObjects or similar function) No

7.3 The Queue Sample Application #


class CQueue
{
public:
    struct ELEMENT
    {
        int m_nThreadNum, m_nRequestNum;
        // Other element data should go here.
    };
    typedef ELEMENT* PELEMENT;

private:
    PELEMENT m_pElements;           // Array of elements to be processed
    int      m_nMaxElements;        // # of elements in the array
    HANDLE   m_h[ 2 ];              // Mutex & semaphore handles
    HANDLE   &m_hmtxQ;              // Reference to m_h[0]
    HANDLE   &m_hsemNumElements;    // Reference to m_h[1]

public:
    CQueue( int nMaxElements );
    ~CQueue();

    BOOL Append( PELEMENT pElement, DWORD dwMilliseconds );
    BOOL Remove( PELEMENT pElement, DWORD dwMilliseconds );
};

Queue.cpp

/******************************************************************************
Module:  Queue.cpp
Notices: Copyright (c) 2000 Jeffrey Richter
******************************************************************************/


#include "..\CmnHdr.h"      /* See Appendix A. */
#include <windowsx.h>
#include <tchar.h>
#include <process.h>        // For _beginthreadex
#include "Resource.h"

///////////////////////////////////////////////////////////////////////////////

class CQueue
{
public:
    struct ELEMENT
    {
        int m_nThreadNum, m_nRequestNum;
        // Other element data should go here
    };
    typedef ELEMENT* PELEMENT;

private:
    PELEMENT m_pElements;           // Array of elements to be processed
    int      m_nMaxElements;        // Maximum # of elements in the array
    HANDLE   m_h[ 2 ];              // Mutex & semaphore handles
    HANDLE   &m_hmtxQ;              // Reference to m_h[0]
    HANDLE   &m_hsemNumElements;    // Reference to m_h[1]

public:
    CQueue( int nMaxElements );
    ~CQueue();

    BOOL Append( PELEMENT pElement, DWORD dwMilliseconds );
    BOOL Remove( PELEMENT pElement, DWORD dwMilliseconds );
};


///////////////////////////////////////////////////////////////////////////////


CQueue::CQueue( int nMaxElements )
    : m_hmtxQ( m_h[ 0 ] ), m_hsemNumElements( m_h[ 1 ] )
{
    m_pElements = (PELEMENT)
        HeapAlloc( GetProcessHeap(), 0, sizeof( ELEMENT ) * nMaxElements );
    m_nMaxElements = nMaxElements;
    m_hmtxQ = CreateMutex( NULL, FALSE, NULL );
    m_hsemNumElements = CreateSemaphore( NULL, 0, nMaxElements, NULL );
}


///////////////////////////////////////////////////////////////////////////////


CQueue::~CQueue()
{
    CloseHandle( m_hsemNumElements );
    CloseHandle( m_hmtxQ );
    HeapFree( GetProcessHeap(), 0, m_pElements );
}


///////////////////////////////////////////////////////////////////////////////


BOOL CQueue::Append( PELEMENT pElement, DWORD dwTimeout )
{
    BOOL fOk = FALSE;
    DWORD dw = WaitForSingleObject( m_hmtxQ, dwTimeout );

    if( dw == WAIT_OBJECT_0 )
    {
        // This thread has exclusive access to the queue

        // Increment the number of elements in the queue
        LONG lPrevCount;
        fOk = ReleaseSemaphore( m_hsemNumElements, 1, &lPrevCount );
        if( fOk )
        {
            // The queue is not full; append the new element
            m_pElements[lPrevCount] = *pElement;
        } else {

            // The queue is full; set the error code and return failure
            SetLastError( ERROR_DATABASE_FULL );
        }

        // Allow other threads to access the queue
        ReleaseMutex( m_hmtxQ );

    } else {
        // Timeout, set error code and return failure
        SetLastError( ERROR_TIMEOUT );
    }

    return ( fOk );     // Call GetLastError for more info
}


///////////////////////////////////////////////////////////////////////////////


BOOL CQueue::Remove( PELEMENT pElement, DWORD dwTimeout ) {

    // Wait for exclusive access to queue and for queue to have element.
    BOOL fOk = ( WaitForMultipleObjects( chDIMOF( m_h ), m_h, TRUE, dwTimeout )
        == WAIT_OBJECT_0 );

    if( fOk )
    {
        // The queue has an element; pull it from the queue
        *pElement = m_pElements[ 0 ];

        // Shift the remaining elements down
        MoveMemory( &m_pElements[ 0 ], &m_pElements[ 1 ],
            sizeof( ELEMENT ) * ( m_nMaxElements - 1 ) );

        // Allow other threads to access the queue
        ReleaseMutex( m_hmtxQ );

    } else {
        // Timeout, set error code and return failure
        SetLastError( ERROR_TIMEOUT );
    }

    return ( fOk );     // Call GetLastError for more info
}


///////////////////////////////////////////////////////////////////////////////


CQueue g_q( 10 );                       // The shared queue
volatile BOOL g_fShutdown = FALSE;      // Signals client/server threads to die
HWND g_hwnd;                            // How client/server threads give status


// Handles to all client/server threads & number of client/server threads
HANDLE g_hThreads[ MAXIMUM_WAIT_OBJECTS ];
int    g_nNumThreads = 0;


///////////////////////////////////////////////////////////////////////////////


DWORD WINAPI ClientThread( PVOID pvParam )
{
    int nThreadNum = PtrToUlong( pvParam );
    HWND hwndLB = GetDlgItem( g_hwnd, IDC_CLIENTS );

    for( int nRequestNum = 1; !g_fShutdown; nRequestNum++ )
    {
        TCHAR sz[ 1024 ];
        CQueue::ELEMENT e = { nThreadNum, nRequestNum };

        // Try to put an element on the queue
        if( g_q.Append( &e, 200 ) )
        {
            // Indicate which thread sent it and which request
            wsprintf( sz, TEXT( "Sending %d:%d" ), nThreadNum, nRequestNum );
        } else {
            // Couldn't put an element on the queue
            wsprintf( sz, TEXT( "Sending %d:%d (%s)" ), nThreadNum, nRequestNum,
                ( GetLastError() == ERROR_TIMEOUT )
                    ? TEXT( "timeout" ) : TEXT( "full" ) );
        }

        // Show result of appending element
        ListBox_SetCurSel( hwndLB, ListBox_AddString( hwndLB, sz ) );
        Sleep( 2500 );      // Wait before appending another element
    }

    return ( 0 );
}


///////////////////////////////////////////////////////////////////////////////


DWORD WINAPI ServerThread( PVOID pvParam )
{
    int nThreadNum = PtrToUlong( pvParam );
    HWND hwndLB = GetDlgItem( g_hwnd, IDC_SERVERS );

    while( !g_fShutdown )
    {
        TCHAR sz[ 1024 ];
        CQueue::ELEMENT e;

        // Try to get an element from the queue
        if( g_q.Remove( &e, 5000 ) )
        {
            // Indicate which thread is processing it, which thread
            // sent it, and which request we're processing
            wsprintf( sz, TEXT( "%d: Processing %d:%d" ),
                nThreadNum, e.m_nThreadNum, e.m_nRequestNum );

            // The server takes some time to process the request
            Sleep( 2000 * e.m_nThreadNum );

        } else {
            // Couldn't get an element from the queue
            wsprintf( sz, TEXT( "%d: (timeout)" ), nThreadNum );
        }

        // Show result of processing element
        ListBox_SetCurSel( hwndLB, ListBox_AddString( hwndLB, sz ) );
    }

    return ( 0 );
}


///////////////////////////////////////////////////////////////////////////////


BOOL Dlg_OnInitDialog( HWND hwnd, HWND hwndFocus, LPARAM lParam )
{
    chSETDLGICONS( hwnd, IDI_QUEUE );

    g_hwnd = hwnd;      // Used by client/server threads to show status

    DWORD dwThreadID;

    // Create the client threads
    for( int x = 0; x < 4; x++ )
        g_hThreads[g_nNumThreads++] =
            chBEGINTHREADEX( NULL, 0, ClientThread, (PVOID) (INT_PTR) x,
                0, &dwThreadID );

    // Create the server threads
    for( x = 0; x < 2; x++ )
        g_hThreads[g_nNumThreads++] =
            chBEGINTHREADEX( NULL, 0, ServerThread, (PVOID) (INT_PTR) x,
                0, &dwThreadID );

    return ( TRUE );
}


///////////////////////////////////////////////////////////////////////////////


void Dlg_OnCommand( HWND hwnd, int id, HWND hwndCtl, UINT codeNotify )
{
    switch( id )
    {
        case IDCANCEL:
            EndDialog( hwnd, id );
            break;
    }
}


///////////////////////////////////////////////////////////////////////////////


INT_PTR WINAPI Dlg_Proc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
    switch( uMsg )
    {
        chHANDLE_DLGMSG( hwnd, WM_INITDIALOG, Dlg_OnInitDialog );
        chHANDLE_DLGMSG( hwnd, WM_COMMAND,    Dlg_OnCommand );
    }
    return ( FALSE );
}


///////////////////////////////////////////////////////////////////////////////


int WINAPI _tWinMain( HINSTANCE hinstExe, HINSTANCE, PTSTR pszCmdLine, int )
{
    DialogBox( hinstExe, MAKEINTRESOURCE( IDD_QUEUE ), NULL, Dlg_Proc );
    InterlockedExchangePointer( (PVOID*) &g_fShutdown, (PVOID) TRUE );

    // Wait for all the threads to terminate & then cleanup
    WaitForMultipleObjects( g_nNumThreads, g_hThreads, TRUE, INFINITE );
    while( g_nNumThreads-- )
        CloseHandle( g_hThreads[ g_nNumThreads ] );

    return ( 0 );
}


//////////////////////////////// End of File //////////////////////////////////

Queue.rc

// Microsoft Developer Studio generated resource script.
//
#include "Resource.h"

#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "afxres.h"

/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS

/////////////////////////////////////////////////////////////////////////////
// English (U.S.) resources

#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
#ifdef _WIN32
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
#pragma code_page(1252)
#endif //_WIN32

/////////////////////////////////////////////////////////////////////////////
//
// Dialog
//

IDD_QUEUE DIALOG DISCARDABLE  38, 36, 298, 225
STYLE WS_MINIMIZEBOX | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
CAPTION "Queue"
FONT 8, "MS Sans Serif"
BEGIN
    GROUPBOX        "&Client threads",IDC_STATIC,4,4,140,216
    LISTBOX         IDC_CLIENTS,8,16,132,200,NOT LBS_NOTIFY |
                    LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP
    GROUPBOX        "&Server threads",IDC_STATIC,156,4,140,216
    LISTBOX         IDC_SERVERS,160,16,132,200,NOT LBS_NOTIFY |
                    LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP
END

/////////////////////////////////////////////////////////////////////////////
//
// Icon
//

// Icon with lowest ID value placed first to ensure application icon
// remains consistent on all systems.
IDI_QUEUE           ICON    DISCARDABLE     "Queue.Ico"

#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//

1 TEXTINCLUDE DISCARDABLE
BEGIN
    "Resource.h\0"
END

2 TEXTINCLUDE DISCARDABLE
BEGIN
    "#include ""afxres.h""\r\n"
    "\0"
END

3 TEXTINCLUDE DISCARDABLE
BEGIN
    "\r\n"
    "\0"
END

#endif  // APSTUDIO_INVOKED


/////////////////////////////////////////////////////////////////////////////
//
// DESIGNINFO
//

#ifdef APSTUDIO_INVOKED
GUIDELINES DESIGNINFO DISCARDABLE
BEGIN
    IDD_QUEUE, DIALOG
    BEGIN
        RIGHTMARGIN, 244
        BOTTOMMARGIN, 130
    END
END
#endif  // APSTUDIO_INVOKED

#endif  // English (U.S.) resources
/////////////////////////////////////////////////////////////////////////////



#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//


/////////////////////////////////////////////////////////////////////////////
#endif  // not APSTUDIO_INVOKED

8 A Handy Thread Synchronization Object Chart #


ObjectWhen NonsignaledWhen SignaledSuccessful Wait Side Effect
Process While process is still active When process terminates (ExitProcess, TerminateProcess) None
Thread While thread is still active When thread terminates (ExitThread, TerminateThread) None
Job When job's time has not expired When job time expires None
File When I/O request is pending When I/O request completes None
Console input No input exists When input is available None
File change notifications No files have changed When file system detects changes Resets notification
Auto-reset event ResetEvent, PulseEvent, or successful wait When SetEvent/PulseEvent is called Resets event
Manual-reset event ResetEvent or PulseEvent When SetEvent/PulseEvent is called None
Auto-reset waitable timer CancelWaitableTimer or successful wait When time comes due (SetWaitableTimer) Resets timer
Manual-reset waitable timer CancelWaitableTimer When time comes due (SetWaitableTimer) None
Semaphore Successful wait When count > 0 (ReleaseSemaphore) Decrements count by 1
Mutex Successful wait When unowned by a thread (ReleaseMutex) Gives ownership to thread
Critical section (user-mode) Successful wait ((Try)EnterCriticalSection) When unowned by a thread (LeaveCriticalSection) Gives ownership to thread

9 Other Thread Synchronization Functions #



9.1 Asynchronous Device I/O #



9.2 WaitForInputIdle #


DWORD WaitForInputIdle(
	HANDLE hProcess,
	DWORD dwMilliseconds
);

9.3 MsgWaitForMultipleObjects(Ex) #


DWORD MsgWaitForMultipleObjects(
	DWORD dwCount,
	PHANDLE phObjects,
	BOOL fWaitAll,
	DWORD dwMilliseconds,
	DWORD dwWakeMask
);

DWORD MsgWaitForMultipleObjectsEx(
	DWORD dwCount,
	PHANDLE phObjects,
	DWORD dwMilliseconds,
	DWORD dwWakeMask,
	DWORD dwFlags
);

9.4 WaitForDebugEvent #


BOOL WaitForDebugEvent(
	PDEBUG_EVENT pde,
	DWORD dwMilliseconds
);

9.5 SignalObjectAndWait #


DWORD SignalObjectAndWait(
	HANDLE hObjectToSignal,
	HANDLE hObjectToWaitOn,
	DWORD dwMilliseconds,
	BOOL fAlertable
);

ReleaseMutex( hMutex );
WaitForSingleObject( hEvent, INFINITE );

// Perform some work.

...

SetEvent( hEventWorkerThreadDone );
WaitForSingleObject( hEventMoreWorkToBeDone, INFINITE );
// Do more work.

...

WaitForSingleObject( hEventWorkerThreadDone );
PulseEvent( hEventMoreWorkToBeDone );

// Perform some work.

...

SignalObjectAndWait( hEventWorkerThreadDone,
    hEventMoreWorkToBeDone, INFINITE, FALSE );
// Do more work.

...



CategoryTranslation

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