Click here to Skip to main content
15,867,594 members
Articles / Desktop Programming / WTL
Article

Thread classes for WTL

Rate me:
Please Sign up or sign in to vote.
4.85/5 (30 votes)
30 May 20064 min read 84.7K   1.5K   77   18
Presents a small set of ATL/WTL classes that simplify working with threads.

Introduction

While ATL and WTL offer useful wrapper classes for most of the various objects a Win32 HANDLE can refer to, threads still have to be created and controlled using the API functions and handles directly. Admittedly, this is not a hard thing to do, but a clean object-oriented design can often help in avoiding bugs in multi-threaded applications.

The class collection I am going to present has often proved very useful to me. It contains handle wrappers for threads, as well as base classes that help implement worker and GUI threads.

The thread handle wrapper classes

There is not much to say about these classes. In the style usually followed by WTL, CThreadT is a wrapper template class around a thread handle, which offers most of the Win32 API functions that take a thread handle as methods.

More conveniently, one will usually use the template instantiations CThread and CThreadHandle: CThread will close the thread handle when it is destroyed, while CThreadHandle won't.

Creating a thread

Thread creation is implemented as the static method CThreadT::Create, which is a wrapper around _beginthreadex (or CreateThread, if the "minimal use of CRT" option is set).

// ...
CThread thread = CThread::Create( (LPTHREAD_START_ROUTINE) 
                                   MyThreadProc, pParam );
// ...

Additional methods of CThread and CThreadHandle

  • CThreadT(HANDLE=NULL, DWORD=0) wraps a CThread instance around the given thread handle and thread ID.
  • There is also a copy constructor, which will call DuplicateHandle on the given handle.
  • The thread handle and ID can be retrieved with GetHandle() and GetId().
  • The methods Open, GetPriority, SetPriority, GetExitCode, GetThreadTimes, IsIOPending, Resume, Suspend, Terminate, and Exit are all wrappers around the respective API functions, their names mostly net of the word "thread".
  • The method Join performs a WaitForSingleObject on the thread handle.

GUI Threads

GUI threads are different from general worker threads in that they have a message queue. This means that we can post messages to a GUI thread using PostThreadMessage.

The template class CGuiThreadT, and its instantiations CGuiThread and CGuiThreadHandle, are just like the CThreadT classes, with an additional method PostThreadMessage for this end.

Causing a GUI thread to quit is usually a matter of posting the WM_QUIT message to its queue. This is done by the PostQuitMessage method. However, this method does not wrap the API of the same name, as the latter posts WM_QUIT to the calling thread, which is not always the same.

The thread implementation classes

As ATL and WTL often differentiate between a handle to an object and its implementation (compare CWindow and CWindowImpl), I have chosen the same design for my thread classes (though the two cases are not perfectly comparable). The class CThreadImpl provides the skeleton for a thread "implementation" class.

Derive your thread class from CThreadImpl<T> and implement the Run method:

C++
class CWorkerThread : public CThreadImpl<CWorkerThread>
{
public:
  DWORD Run()
  {
    // Do something useful...

    return 0;
  }
};

//
// In some other function, that is called from your main thread:

CWorkerThread* pThread = new CWorkerThread;

The return value of Run is the thread's exit code, like in the standard Win32 ThreadProc.

If you create an instance of CWorkerThread, it will start running immediately. If you want to start it later, you can pass CREATE_SUSPENDED to CThreadImpl's constructor and call Resume later.

Note that the constructor runs in the creating thread, so some of the thread initialization may have to be moved to the run method.

Example

class CWorkerThread : public CThreadImpl<CWorkerThread>
{
public:
  CWorkerThread()
    : CThreadImpl<CWorkerThread>(CREATE_SUSPENDED)
  { }

  BOOL Initialize()
  {
    // Perform initialization.
    return TRUE;
  }

  DWORD Run()
  {
    if ( !Initialize() )
      return 1;

    // Do something useful...
    
    return 0;
  }
};

//
// In some other function, that is called from your main thread:

CWorkerThread* pThread = new CWorkerThread;
pThread->Resume();

Implementation of GUI threads

The class CGuiThreadImpl uses the WTL class CMessageLoop to manage a message loop. However, since CAppModule wants to know about all CMessageLoops in the process, this means you have to pass a pointer to your CAppModule instance in the constructor.

To implement a GUI thread, derive your class from CGuiThreadImpl and (optionally) override the following methods:

  • BOOL InitializeThread() to perform thread initialization. This is, for example, a good place to create windows. Return FALSE to stop the thread.
  • void CleanupThread(DWORD) to perform cleanup tasks. The DWORD parameter is the exit code from the message loop.

Handling messages

You can also add a message map to your thread class using the standard macro, BEGIN_MSG_MAP. However, these will only be called for messages that are not directed to a window, i.e., where the hWnd parameter is NULL.

Remember that you can access the thread's message loop using CAppModule::GetMessageLoop(), so you can, for example, install additional CMessageFilters. The place to do this would be the InitializeThread and CleanupThread methods.

Example

The following example shows a simple GUI thread class which creates a timer and responds to the WM_TIMER message.

#include "Thread.h"

class CTimerThread : public CGuiThreadImpl<CTimerThread>
{
  BEGIN_MSG_MAP(CTimerThread)
    MESSAGE_HANDLER(WM_TIMER, OnTimer)
  END_MSG_MAP()

private:
  UINT_PTR m_nTimerId;

public:
  CTimerThread(CAppModule* pModule)
    : CGuiThreadImpl<CTimerThread>(pModule)
  { }

  BOOL InitializeThread()
  {
     m_nTimerId = ::SetTimer(NULL, 0, 1000, NULL);
     return (m_nTimerId != 0);
  }

  void CleanupThread(DWORD)
  {
    ::KillTimer(NULL, m_nTimerId);
  }

  LRESULT OnTimer(UINT, WPARAM, LPARAM, BOOL&)
  {
    ::MessageBeep(MB_ICONASTERISK);
    return 0;
  }
};

The "timer thread" has to be created and stopped from the main thread:

class CMainFrame : ...
{
  CTimerThread* m_pTimerThread;

  // ...

  LRESULT OnCreate(LPCREATESTRUCT)
  {
    // ...
    m_pTimerThread = new CTimerThread(&_Module);
    // ...
  }

  void OnDestroy()
  {
    // ...
    g_pTimerThread->PostQuitMessage();
    g_pTimerThread->Join();
    delete g_pTimerThread;
    // ...
  }
};

Using the classes

All the thread classes are contained in a single header file, Thread.h, which you can download using the link given at the start of the article. To facilitate the classes, you just need to include Thread.h in your project.

If you would like to use the classes in a ATL-but-not-WTL project, you will need to remove all the GUI-thread related sections from the code. The other classes (CThreadT and CThreadImpl) will work with "pure" ATL as well.

Conclusion

Using the set of classes presented in this article, it is possible to achieve a cleaner, more object-oriented design for multithreaded applications. The template design similar to the one found in other ATL/WTL classes makes it easy to understand and integrate.

Revision history

  • 06-30-2006
    • Original article.
  • 06-31-2006
    • CThreadT::Create now calls _beginthreadex if possible (depending on _ATL_MIN_CRT).
    • Added method CThreadT::Exit.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Software Developer (Senior) Accenture
Germany Germany
Till is living in Munich, Germany, and works as an IT consultant. His current focus is mainly on Java Enterprise projects, but tries to stay up to date with the latest .NET developments.

Comments and Discussions

 
GeneralMy vote of 5 Pin
Member 199691720-Apr-12 6:50
Member 199691720-Apr-12 6:50 
GeneralConstructor Pin
barto23-Nov-10 10:32
barto23-Nov-10 10:32 
GeneralRe: Constructor Pin
Till Krullmann27-Nov-10 23:52
Till Krullmann27-Nov-10 23:52 
GeneralATL library DOES have a thread wrapped class:CWorkerThread Class Pin
David_LoveCpp13-Apr-10 20:16
David_LoveCpp13-Apr-10 20:16 
GeneralRe: ATL library DOES have a thread wrapped class:CWorkerThread Class Pin
Till Krullmann28-Nov-10 0:12
Till Krullmann28-Nov-10 0:12 
Generalerror C2488: '_ThreadProcThunk' Pin
Member 92643716-Mar-09 5:08
Member 92643716-Mar-09 5:08 
GeneralProgress dialog with your thread Pin
Rafael Franco7-Feb-07 6:41
Rafael Franco7-Feb-07 6:41 
GeneralRe: Progress dialog with your thread Pin
Till Krullmann7-Feb-07 7:10
Till Krullmann7-Feb-07 7:10 
Questiondead lock? Pin
Satie Zhao7-Jun-06 22:13
Satie Zhao7-Jun-06 22:13 
AnswerRe: dead lock? Pin
Thanda.Dude3-Dec-09 16:42
Thanda.Dude3-Dec-09 16:42 
QuestionCOM? Pin
JBurkey1-Jun-06 7:20
JBurkey1-Jun-06 7:20 
AnswerRe: COM? Pin
olegxxx5-Jun-06 20:13
olegxxx5-Jun-06 20:13 
GeneralGood stuff Pin
yafan30-May-06 14:15
yafan30-May-06 14:15 
GeneralRe: Good stuff Pin
Nemanja Trifunovic31-May-06 1:21
Nemanja Trifunovic31-May-06 1:21 
GeneralRe: Good stuff Pin
Till Krullmann31-May-06 1:55
Till Krullmann31-May-06 1:55 
GeneralRe: Good stuff Pin
Nemanja Trifunovic31-May-06 2:41
Nemanja Trifunovic31-May-06 2:41 
GeneralRe: Good stuff Pin
Till Krullmann31-May-06 3:14
Till Krullmann31-May-06 3:14 
GeneralRe: Good stuff Pin
Pablo Aliskevicius5-Jun-06 21:18
Pablo Aliskevicius5-Jun-06 21:18 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.