Contents:
- 1 Thread Synchronization With Kernel Objects
- 2 Wait Functions
1 Thread Synchronization With Kernel Objects #
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 );
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 );
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
);
HANDLE CreateMutex(
PSECURITY_ATTRIBUTES psa,
BOOL fInitialOwner,
PCTSTR pszName
);
HANDLE OpenMutex(
DWORD fdwAccess,
BOOL bInheritHandle,
PCTSTR pszName
);
BOOL ReleaseMutex( HANDLE hMutex );
7.2 Mutexes vs. Critical Sections #
| Characteristic | Mutex | Critical 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 #
| Object | When Nonsignaled | When Signaled | Successful 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 #
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
);
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