Traditionally, Win32 threads are implemented using C-style global functions which accept an LPVOID parameter, return a DWORD value, and have the stdcall calling convention. This is fine for simple and small threads, but it can cause difficulties when writing a complicated thread which needs to call several other functions, or designing an object-oriented thread model. If one is limited solely with this option, she either has to handcraft some mechanism to turn the old-style free thread proc function into C++ code using classes, which can be repeating, and thus boring, and thus very error-prone.
There are solutions to this problem. One can use the MFC CWinThread class mechanism together with AfxBeginThread( ), or can write her own threading mechanism in C++. I have personally chosen the second path for two reasons. The first one is MFC CWinThread mechanism uses a message loop internally, and many threads don’t need one, so inviting CWinThread into the picture of your project can both cause inefficiency as well as needless complexity. The second reason is I don’t like the MFC type detection model (using the RUNTIME_CLASS macro) and I think when the language offers this feature in form of C++ templates, one should not invent a new mechanism of his own.
The CWin32Thread class together with two thread creator classes provide the functionality I’ve been after. CWin32Thread serves two purposes. It wraps a Win32 thread handle (a HANDLE data type) and provides convenience member functions to manipulate it, and it has the role of a base class for your own thread types. It is an abstract base class, which means it can’t be instantiated directly. It is copyable, however, so that your derived thread classes can have copy constructors, as well as assignment operators of their own, if appropriate.
Writing your own thread class is extremely easy. You typically derive from CWin32Thread like this:
class CThreadClass :
You should always override the CWin32Thread::Main( ) function, because it’s a pure virtual function, and without overriding it your thread class can’t be instantiated. This is the thread’s main function, which, upon returning, causes the thread to terminate. The UINT return value of this function is assigned to the thread’s return value when all the job is done. There are two other virtual functions, which you can override. OnInitialize( ) is the function which should do the thread’s initialization. If this function returns false, then the thread is terminated, assuming some initialization code has failed. The OnTerminate( ) function is supposed to do the thread’s cleanup and anything else which needs to be done during thread’s termination. These two functions can be left unimplemented, and in that case their default base class versions are called which simply do nothing. Another point to pay attention to is the thread class can be default constructible, and also derive publicly from CWin32Thread; otherwise the code will fail to compile.
CWin32Thread has a lot of other convenience helper functions, which are adequately documented in Win32Thread.h (which comes in the ZIP package downloadable from the bottom of this article – so I won’t describe them here again.
There are two other utility classes in the library which are used to create threads. The first one to describe here is a template class called CWin32ThreadCreator. This class has a single template parameter which should be assigned the name of your thread class. It has a CreateThread( ) static function which creates a thread of the specified class, and returns a pointer to it, or NULL if it encounters any errors. To see a documentation of the parameters this function accepts, check out the Platform SDK documentation for the function ::CreateThread( ). You create a thread of your thread class by simply writing:
CThreadClass * pThread = CWin32ThreadCreator< CThreadClass >::CreateThread();
Please remember that the library automatically deletes the returned pointer, so you should never call delete on it yourself, or the results are undefined. Also, please don’t store the pointer for later use, because there’s no guarantee when the spawned thread terminates and the pointer is deleted. If you need to store the information about the thread in some variable (for example, m_myThread), define it as a CWin32ThreadInfo variable, and write:
// start the thread as paused
CWin32ThreadInfo class is the class responsible for wrapping the thread handle and information, and is a base class of the CWin32Thread class. Alternatively, you can define m_myThread of type CThreadClass, which, indirectly would derive from CWin32ThreadInfo.
Another class which can be used to create threads is CWin32ProcThreadCreator. This class is used to create threads which use an old style free function as their thread procedure. This class is not a template class, and provides a static CreateThread( ) function which accepts several parameters. The first one is the address of the free function used to implement the thread’s procedure, and the second one is the LPVOID parameter passed to the thread procedure routine. The rest of the parameters are identical to those of CWin32ThreadCreator::CreateThread( ). You use this class to create a thread like this:
CWin32Thread * pThread = CWin32ProcThreadCreator::CreateThread( &::ThreadProc, pParams );
And then you can test pThread against NULL to see if the thread creation has failed or not. Needless to say, ThreadProc would be declared as:
DWORD WINAPI ThreadProc(LPVOID pv);
This is pretty much everything you need to know to use the library. For more information, consult the header file (Win32Thread.h) and the source file (Win32Thread.cpp).
The sample application which is included in the ZIP package downloadable from the bottom of this article in an MFC dialog application with two threads, which are completely identical, except that one is class based and the other is procedure based. See the source code of the application (especially TestProcThread.h/cpp and TestClassThread.h/cpp to see what changes should be made to the code when converting from a procedure based thread to a class based one, and vice versa.