When Main_OnCreate constructs the secondary threads, it passes a pointer to the StartThread function in each call to CreateThread. StartThread becomes the main procedure for all the threads. They begin and end executing here.
If bUseMutex is TRUE, then the threads will wait to acquire the mutex before they draw, and only one thread will draw at a time.
/*————————————————————————————————————————————————————————————————————
START THREAD
This is called when each thread begins execution.
—————————————————————————————————————————————————————————————————————*/
LONG StartThread ( LPVOID lpThreadData )
{
DWORD *pdwThreadID; // pointer to a DWORD for storing thread’s ID
DWORD dwWait; // return value from WaitSingleObject
// retrieve the thread’s ID
pdwThreadID = lpThreadData;
// draw continuously until bTerminate becomes TRUE
while( ! bTerminate )
{
if (bUseMutex) // are we using the mutex?
{
// draw when this thread gets the mutex
dwWait = WaitForSingleObject( hDrawMutex, INFINITE );
if( dwWait == 0 )
{
DrawProc( *pdwThreadID ); // draw rectangles
ReleaseMutex( hDrawMutex ); // let someone else draw
}
}
else
{
// not using mutex; let the thread draw
DrawProc( *pdwThreadID );
}
}
// this return statement implicitly calls ExitThread
return( 0L );
}
DrawProc draws a batch of rectangles. However, GDI (Graphics Device Interface) calls do not always occur immediately to avoid the overhead of many small messages passing between a program and the Win32 subsystem. For this reason, graphics commands are held in a queue and periodically flushed. These delays somewhat exaggerate the effect of changing priorities in the Threads demo.
/*————————————————————————————————————————————————————————————————--—————
DRAW PROC
Draw five random rectangles or ellipses.
—————————————————————————————————————————————————————————————————---————*/
void DrawProc ( DWORD dwID )
{
...
if (bUseMutex)
{
iTotal = 50; // if only one thread draws at a time,
} // let it draw more shapes at once
else
{
iTotal = 1;
}
// reseed the random generator
srand( iRandSeed++ );
// get the window’s dimensions
bError = GetClientRect( hwndChild[dwID], &rcClient );
if( ! bError ) return;
cxClient = rcClient.right - rcClient.left;
cyClient = rcClient.bottom - rcClient.top;
// do not draw if the window does not have any dimensions
if( ( ! cxClient ) || ( ! cyClient ) )
{
return;
}
// get a device context for drawing
hDC = GetDC( hwndChild[dwID] );
if( hDC )
{
// draw the five random figures
for( iCount = 0; iCount < iTotal; iCount++ )
{
iRectCount[dwID]++;
// set the coordinates
xStart = (int)( rand() % cxClient );
xStop = (int)( rand() % cxClient );
yStart = (int)( rand() % cyClient );
yStop = (int)( rand() % cyClient );
// set the color
iRed = rand() & 255;
iGreen = rand() & 255;
iBlue = rand() & 255;
// create the solid brush
hBrush = CreateSolidBrush( // avoid dithered colors
GetNearestColor( hDC, RGB( iRed, iGreen, iBlue ) ) );
hbrOld = SelectBrush( hDC, hBrush );
// draw a rectangle
Rectangle( hDC, min( xStart, xStop ), max( xStart, xStop ),
min( yStart, yStop ), max( yStart, yStop ) );
// delete the brush
DeleteBrush( SelectBrush(hDC, hbrOld) );
}
// If only one thread is drawing at a time, clear
// the child window before the next thread draws
if( bUseMutex )
{
SelectBrush( hDC, GetStockBrush(WHITE_BRUSH) );
PatBlt( hDC, (int)rcClient.left, (int)rcClient.top,
(int)rcClient.right, (int)rcClient.bottom,
PATCOPY );
}
// release the HDC
ReleaseDC( hwndChild[dwID], hDC );
}
return;
}
After you have run the Threads demo and experimented with priorities, turned the mutex on and off, and suspended and resumed a few threads, you might want to try running the Process Viewer that comes with the Win32 SDK. In Figure 14.4, you can see what the Process Viewer says about Threads. Note that it shows five threads for the program because it includes the primary thread as well. Browsing with Process Viewer gives you a better sense of how Windows 98 works.
Figure 14.4: Examining threads using the Win32 SDK Process Viewer
This chapter has explored the use of multiple threads. The Threads demo gives a practical example of threads in action. In this discussion, I’ve mentioned processes and interprocess communication through pipes and shared memory. In Chapter 15, you’ll learn how to create and manage processes.