Thursday, November 15, 2007

A Concurrency Sub-Pattern

I often find that I need to have some work done periodically in the background for some length of time.  The frequency varies but basically a background thread needs to run in a loop, indefinitely, and wake up every so often to do the work.

 

Since I don't know in advance when I'm going to need to terminate the background thread, I've found that I can use a single Event to kill 2 birds with 1 stone.

 

Declare a stop event - make sure it's unsignaled by default.  In the Win32 world this is done via CreateEvent().

 

In the thread loop, instead of sleeping for a set period of time then doing the work, use a timed wait on the StopEvent.  If it times out, do the work, otherwise exit the loop.  Something like

 

while ( true )

{

   WaitForSingleObject(stopEvent, SleepTime);

   if ( timed out )

     do work

   else

      break out of loop

}

 

When it's time to stop the background thread all you have to do is signal the stop event.  If it's signaled while the thread is doing its work then as soon as it's done the thread will stop.

 

If the application needs to know when the background thread has stopped, at least in the Windows world, all you need to do is poll GetExitCodeForThread() (have your background thread func return 0 when it's done).  This has the advantage of not introducing any additional event objects (e.g., an "I'm done" event) which plays into my preference for using the existing framework as much as possible.  On the downside it does require some busy waiting (in between checks to GetExitCodeForThread()).  And if your background thread gets "stuck" your foreground thread may get stuck waiting on it forever.

 

Another rule of thumb I've found useful is "Never Lock and Block()".  As long as you make sure that your background thread doesn't block then there's no need to worry about locking up the foreground thread waiting on the background thread to terminate.

No comments :

Post a Comment