Introduction
Over the past few years, multithreaded apps have become the mainstay of my development repertoire. I've even found myself writing multithreaded apps where it wasn't needed. Worker threads allow Win32 applications to improve the user experience and increase performance.
With this varied experience, I've come up with a set of patterns that I find myself using frequently (OK, so they're not patterns in the classical sense; more of paradigms). I've written classes which streamline the use of these paradigms in all situations.
In part 2 of this three article series, I will show the most frequently used of these paradigms: the Exitable Thread paradigm. The majority of worker threads perform the same action over and over again within some kind of loop. Whether it is processing a number of different files, exporting data from one format to another, or reading message coming across the wire, these actions have a very specific rhythm.
Description of the Exitable Thread pattern
The basis of the Exitable Thread pattern is the pairing of a thread with a Win32 Event object. This Event object is unsignaled until an external event (the user pressing a cancel button, app shutdown, etc). signals the event. At the beginning or end of each iteration of the worker thread, the thread checks the exit event. If the event is set, it exits the loop, performing any needed cleanup.
The ExitableThread class
The ExitableThread
class inherits from the Thread
class created in article #1 of this series. This allows us to capitalize on the code for the basic thread creation and method invocation, while adding the Exit Event object and associated methods.
template<class T, class P>
class ExitableThread : public Thread<T, P>
{
public:
typedef void (T::*ThreadFunc)( P );
ExitableThread();
virtual ~ExitableThread();
void ExitThread();
bool ExitThreadAndWait( DWORD timeoutMS = 5000 );
bool IsExitEventSet();
};
The comments speak for the code. The new ExitThread()
methods allow other threads to wait on the encapsulated thread to exit. These methods set the thread's exit event. The ExitThread()
method sets the event and immediately returns to the caller, while the ExitThreadAndWait()
waits for the thread handle to become signaled. If it does not exit before the specified timeout elapses, the thread is terminated.
Using the ExitableThread class
The ExitableThread
class is used by classes in the same way that the Thread
parent class is. However, the worker thread's method should be an iterative process, which periodically calls the IsExitEventSet()
method to see if it should exit.
void TestClass::DoExitableWork( int i )
{
Files::iterator it;
for (it = files.begin(); it != files.end(); it++)
{
ProcessFile( *it );
if (m_thread.IsExitEventSet())
break;
}
}
Conclusion
The Exitable Thread is a pattern (or paradigm) which helps control worker threads and their lifetime. When an external circumstance warrants a thread to exit, the Exitable Thread pattern allows the worker thread to get the message and cleanup properly and release any resources. The ExitableThread
class allows this pattern to easily be inserted wherever an iterative worker thread is needed.
In the final part of this short series, we will look at how to extend both the Thread
and ExitableThread
classes into a powerful set of classes, which removes any assumptions made about the caller and callee.
History
- 2006.11.22 - First revision.