Click here to Skip to main content
Click here to Skip to main content

Thread classes for WTL

By , 30 May 2006
 

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:

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

About the Author

Till Krullmann
Software Developer (Senior) Accenture
Germany Germany
Member
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.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralMy vote of 5memberMember 199691720 Apr '12 - 6:50 
good job
GeneralConstructormemberbarto23 Nov '10 - 10:32 
Thanks for the article.
 
However there's a big caveat concerning constructors:
The problem is not that the constructor is running in the creator's thread, but that CThreadImpl<>'s constructor calls ::CreateThread (which starts the thread immediately) BEFORE the constructor of the derived class is even called. So Run() is called without the constructor even completed, resulting in corrupted class members.
 
I'm currently evaluating if there's a general solution to this problem. (Except from the obvious, which is CREATE_SUSPENDED and Resume).
GeneralRe: ConstructormemberTill Krullmann27 Nov '10 - 23:52 
Hi barto,
 
thanks for your reply, it's a good point. I haven't been using this code in a while, but I think this was why I didn't use the constructors to initialize thread data. Rather, I wrote an Initialize() method that is called from the thread proc, like in the code example. Maybe I should have emphasized this more in the article.
GeneralATL library DOES have a thread wrapped class:CWorkerThread ClassmemberDavid_LoveCpp13 Apr '10 - 20:16 
Updated: November 2007
 
This class creates a worker thread or uses an existing one, waits on one or more kernel object handles, and executes a specified client function when one of the handles is signaled.
 

template <
class ThreadTraits = DefaultThreadTraits
>
class CWorkerThread

 
Parameters
ThreadTraits
The class providing the thread creation function, such as CRTThreadTraits or Win32ThreadTraits.
GeneralRe: ATL library DOES have a thread wrapped class:CWorkerThread ClassmemberTill Krullmann28 Nov '10 - 0:12 
Hi David,
 
I'm not sure if CWorkerThread was there back when I wrote this article. I think I used Visual Studio 2003 (and the then-current ATL version) on the project from which I drew the code.
 
Of course, I encourage everyone to use or extend what the platform provides before resorting to third-party libraries or "reinventing the wheel".
 
Still, when looking at CWorkerThread, I notice that its focus is on background worker threads and COM, while what I mainly needed was adding message maps to threads using the standard macros in a non-COM WTL application. I think the separation between CThreadImpl and CGuiThreadImpl came only later to create a cleaner design.
Generalerror C2488: '_ThreadProcThunk'memberMember 92643716 Mar '09 - 5:08 
error C2488: '_ThreadProcThunk' : 'naked' can only be applied to function definitions
GeneralProgress dialog with your threadmemberrafranco7 Feb '07 - 6:41 
Hi,
 

First, I would like thank you for this article. Very nice job.
 
I'm working on a threaded progress dialog to be shown when my application will do some long time job. Do you have any sample like that?
I found an article about threaded progress dialog here on The Code Project, but it is MFC not WTL. Could you help me?
 
Thanks
 
Rafael Franco
rafaelfc1208@hotmail.com
GeneralRe: Progress dialog with your threadmemberTill Krullmann7 Feb '07 - 7:10 
Hi Rafael,
 
Do you really want the actual dialog to be in its own thread? It sounds like you should do the "long time job" in a non-GUI worker thread, and post regular "progress changed" events to a window in the main GUI thread. It's convenient to use the PBM_SETPOS message for this, so you can send your events directly to a progress bar, or to some other window that in turn updates the progress bar.
 
I've done something similar with a CWorkerThread class which accepts generic CTask objects. Some excerpt is given below. It's from an OpenInventor project, so I used some of its constructs, but maybe you get the point.
 
void CWorkerThread::SetProgressBar(HWND hwndProgress)
{
	::InterlockedExchangePointer( (void**) &m_wndProgress.m_hWnd, (void*) hwndProgress );
 
	if ( m_wndProgress.IsWindow() )
		m_wndProgress.GetRange( &m_ProgressRange );
}
 

BOOL CWorkerThread::SetProgressMarquee(BOOL bMarquee)
{
	if ( m_wndProgress.IsWindow() )
	{
#if (_WIN32_WINNT >= 0x0501)
		m_wndProgress.ModifyStyle(bMarquee ? 0 : PBS_MARQUEE, bMarquee ? PBS_MARQUEE : 0);
		return m_wndProgress.PostMessage( PBM_SETMARQUEE, (WPARAM) bMarquee,
			(LPARAM) ASYNC_PROGRESS_MARQUEE_UPDATE_TIME );	
#endif
	}
	return FALSE;
}
 

void CWorkerThread::UpdateProgress(float fProgress)
{
	if ( m_wndProgress.IsWindow() )
	{
		ATLASSERT( m_ProgressRange.iHigh > m_ProgressRange.iLow );
		ATLASSERT( fProgress >= 0.0f && fProgress <= 1.0f );
 
		int nRange = m_ProgressRange.iHigh - m_ProgressRange.iLow;
		int nProgress = m_ProgressRange.iLow + (int) (fProgress * nRange);
 
		m_wndProgress.PostMessage( PBM_SETPOS, (WPARAM) nProgress, 0L );
	}
}
 

void CWorkerThread::OnProgressChanged(CWorkerThread* pEvaluator, SoSensor* pSensor)
{
	SoField* pField = ((SoFieldSensor*) pSensor)->getAttachedField();
 
	ATLASSERT( pEvaluator != NULL );
	ATLASSERT( pField != NULL );
	ATLASSERT( pField->isOfType(SoSFFloat::getClassTypeId()) );
 
	pEvaluator->UpdateProgress( ((SoSFFloat*) pField)->getValue() );
}

Questiondead lock?memberSatie Zhao7 Jun '06 - 22:13 
I used the CGuiThreadImpl help class and your timer example to write a test program on win2003 and vc2005, and found deadlock undefinitely.
 
I created a window which calls your example , immediately the window was destroyed(Consecutively called the PostQuitMessage() , Join() in CMainFrame::OnDestroy)
 
I found when PostQuitMessage is executed the timer thread may not yet step into its message loop . At times the WM_QUIT message is not posted to the thread message loop, so WaitForSingleObject will not return forever and the application is deadlocked.
 
I hope you can get what i said.

AnswerRe: dead lock?memberVedicAnand3 Dec '09 - 16:42 
I too found out the same. though it can be well taken care off but should be considered before waitfunctions. for me ondestroy actually finishes off the main app but the process table shows the process running, i'm thinking it could be on zombie state.
QuestionCOM?memberJBurkey1 Jun '06 - 7:20 
Just a suggestion, but why not have a template argument for the thread apartment? Not a big thing, but it keeps the main thread function just a little bit cleaner by removing another piece of orthogonal code.
 
Nice job, though. It's good to see students using WTL. I've interviewed a bunch of guys coming out of school who think MFC is a dandy framework for everything, and it takes literally years to break them of the habit.
 
J

AnswerRe: COM?memberolegxxx5 Jun '06 - 20:13 
You have met good students however my experience shows that they think that .NET is a dandy framework for everything.
GeneralGood stuffmemberyafan30 May '06 - 14:15 
Just wondering why you did not use _beginthread instead of CreateThread since _beginthread intializes a
CRT correctly, or so the documentation asserts.
 

 
Thanks,
 
-y
GeneralRe: Good stuffmemberNemanja Trifunovic31 May '06 - 1:21 
yafan wrote:
Just wondering why you did not use _beginthread instead of CreateThread since _beginthread intializes a
CRT correctly, or so the documentation asserts.

 
Probably because WTL is often used without CRT. But I agree, it should check for ATL_MIN_CRT macro and use CreateThread only if it is defined, otherwise _beginthreadex (not _beginthread Wink | ;) )
 
And I agree - very good article. 5 from me.
 


My programming blahblahblah blog. If you ever find anything useful here, please let me know to remove it.
GeneralRe: Good stuffmemberTill Krullmann31 May '06 - 1:55 
Thanks a lot for your feedback. I usually hardly make any CRT calls in WTL projects, so using _beginthreadex didn't come to my mind. But I guess it can't hurt...

Of course, now the distinction between ExitThread and _endthreadex becomes also necessary, so I added a new Exit method (see article), which should of course only be called from within Run().

However, I'm not sure if all the thread functions work indifferently on the CRT thread handles; the documentation mentions ResumeThread, that means SuspendThread will also work. I guess most of the functions just read or modify some part of the thread control block the handle points to, so there should be no difference. But what about TerminateThread? There is no CRT equivalent.

GeneralRe: Good stuffmemberNemanja Trifunovic31 May '06 - 2:41 
Till Krullmann wrote:
I'm not sure if all the thread functions work indifferently on the CRT thread handles

 
Don't worry about that. _beginthreadex returns a good ol'
HANDLE and you can use it as you would use one from CreateThread (well, except that you will want to use _endthreadex rather than ExitThread to avoid memory leaks from CRT).
 
As for TerminateThread, it is usually a bad idea, CRT or not. Why do you need it?
 


My programming blahblahblah blog. If you ever find anything useful here, please let me know to remove it.
GeneralRe: Good stuffmemberTill Krullmann31 May '06 - 3:14 
Nemanja Trifunovic wrote:
As for TerminateThread, it is usually a bad idea, CRT or not. Why do you need it?

 
Of course it's a bad idea, and I never use it... I don't even use SuspendThread and ResumeThread. I prefer to build some twisted synchronization constructs that take ages to debug Smile | :)

However, this code is intended as a library, and there may be people who occasionally like terminating their threads and find this feature useful.

GeneralRe: Good stuffmemberPablo Aliskevicius5 Jun '06 - 21:18 
Good stuff indeed...
 
One thing that I missed is a way for a 'manager' thread to send commands to a non-UI worker thread.
 
Lately, I've been using PostThreadMessage for non-UI threads, with WM_COMMAND with a pointer for LPARAM, and WM_QUIT to stop the thread; this is a non-blocking way to do it.
A couple of years ago, I used to create a couple of events for each worker thread; when the 'command' event was set, the thread would execute a command; when the 'done' event was set, the thread would exit its main loop.
 
I've seen somewhere thread commands put in a queue, which was locked by the manager thread while pushing one, and by the worker thread when popping. Whole lot of locking goin' on...
 
In any case, an overridable SendCommand(LPVOID) (or better, SendCommand(T::argument_type *) function would make a nice addition.
 
Needless to mention, you've got my 5.

 
Pablo_A

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

Permalink | Advertise | Privacy | Mobile
Web03 | 2.6.130523.1 | Last Updated 30 May 2006
Article Copyright 2006 by Till Krullmann
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid