Thread Procedures

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.

© 1998 SYBEX Inc. All rights reserved.