Click here to Skip to main content
6,635,160 members and growing! (14,400 online)
Email Password   helpLost your password?
General Programming » Threads, Processes & IPC » Threads     Intermediate

Encapsulating Win32 threads in C++

By Ciprian Miclaus

This article presents a class to encapsulate threads, leaving the user to focus on project details.
VC6, eVC 3.0, eVC 4.0, Windows, Win Mobile, Mobile, Visual Studio, Dev
Posted:26 Jul 2001
Views:162,993
Bookmarked:32 times
Announcements
Loading...
 
Search    
Advanced Search
Add to IE Search
printPrint   add Share
      Discuss Discuss   Broken Article?Report  
34 votes for this article.
Popularity: 6.01 Rating: 3.92 out of 5
2 votes, 7.1%
1
1 vote, 3.6%
2
2 votes, 7.1%
3
7 votes, 25.0%
4
16 votes, 57.1%
5

Intent

Encapsulating Win32 threads in a C++ class, easy to subclass and reuse. Hide the details of threads from users so they can focus on the project details.

Motivation

Object oriented languages like C++ have their strength in their ability to encapsulate the representation and implementation of an object, so that programming is focused on a higher level. We say we're programming at interface level rather than at function level. However most OSes haven't been design with C++ in mind, they were usually implemented using non-OO approaches. That's why sometimes it can be tricky to encapsulate some platform dependent resources, like threads for instance. My approach covers Win32 threads.

Win32 threads

To create another thread in the same process, one has in Win32 couple of API functions that handle threads. However they are C and not C++ API. We can easily notice C idioms like callback functions, conversions to and from void* to other types and so on. Let's take a look at how CreateThread, the API function that creates a thread in Win32 looks like. It's prototype it's shown below:

	HANDLE CreateThread(
		LPSECURITY_ATTRIBUTES lpThreadAttributes, 
		DWORD dwStackSize, 
		LPTHREAD_START_ROUTINE lpStartAddress, 
		LPVOID lpParameter, 
		DWORD dwCreationFlags, 
		LPDWORD lpThreadId
	);

lpStartAddress is a pointer to a callback function that will run in the new thread, and lpParameter is a parameter of type void* passed to the new thread. Passing a pointer to a callback function however it's not in the spirit of OOP, and it comes like a serious impediment if we want to encapsulate threads in classes. The callback function required to be passed to CreateThread looks like this:

	DWORD WINAPI ThreadProc(
		LPVOID lpParameter
	);

We'll notice this prototype will keep us from having this callback function as a member of a class, as it's member functions are passed a hidden parameter: this. What's to be done then? Did we fail miserably? Not yet. We cannot use member function and we've seen why, however classes have static methods, which have single instances for all objects. They are connected to classes rather than to objects. That's why they are not passed this as a parameter. So a static function becomes an interesting candidate for a callback function. However there's one small problem, if we have our thread in a static method, then no matter how many objects of that class we'll have, there will be only one thread, as a static method has a single instance per class. This is not what we want. We want our working thread to be a member method, easy to override by subclasses, and all this workaround to be transparent for the clients. Can we do that? Yes we can. If you look at ThreadProc, you'll notice it can be passed one void* parameter. Nothing prevents us from sending it (void*)this, and in ThreadProc we just call our working method, now that we have this pointer. The code for doing that will look like:

//here we create the thread

HANDLE CThread::CreateThread ()
{
	return ::CreateThread ((NULL, 0, (unsigned long (__stdcall *)(void *))this->runProcess, 
				(void *)this, 0, NULL);
}	

//static method

int CThread::runProcess (void* pThis)
{
	return ((CThread*)(pThis))->Process();
}

//our working method, virtual, overridable

int CThread::Process ()
{
	//will work in another thread

}

So far so good. We managed to provide an encapsulation of threading mechanism, so users will simply have to implement their own Process, and then call CreateThread member function. It's even easier to provide reusability, as a user can simply inherit from our class defined above, CThread, and simply implement Process, and then call CreateThread, and they have a thread simple as that. However there's a small issue to note here: Let's say we have a subclass of CThread, named CMyThread. In CreateThread will convert this (which is of type CMyThread*) to void* and pass it to runProcess, where we reconvert this to CThread. C++ standard states that if you convert a type X* to void*, then only a conversion back, to the same type X* is permitted. Other conversions result in an undefined behaviour. That simply means we've done something wrong. How can we fix that? Well, with a small workaround.

struct workAround {
	CThread* this_thread;
};

//we pass a workAround struct instead of this

HANDLE CThread::CreateThread ()
{
	workAround* wA	= new workAround;
	wa->this_thread	= this;
	return ::CreateThread ((NULL, 0, (unsigned long (__stdcall *)(void *))this->runProcess, 
				(void *)wa, 0, NULL);
}	

//static method

int CThread::runProcess (void* pThis)
{
	workAround* wA	= (workAround*)pThis;
	//this will call the appropriate method, as Process is a virtual method

	CThread* thread = wA->this_thread;
	delete wA;
	return thread->Process();
}

This time we're allright as we're converting to and from the same type (struct workAround). Finally to be in the spirit of C++ we'll use C++ conversion, instead of C conversions. Eg: instead of (void*)wA we'll have static_cast<void*>(wA)

Conclusions

Due to the fact most nowadays OSes are not object oriented, as a C++ we usually have to find workarounds when we need to encapsulate platform dependent resources. We have to apply different tricks to achieve that, but once we encapsulated it, it's very simple to use and reusable with considerable less effort. Threads in Win32 are a good examples in that direction. You can further study the source code to get a deeper insight. Happy programming!

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

Ciprian Miclaus


Member

Location: United States United States

Other popular Threads, Processes & IPC articles:

Article Top
You must Sign In to use this message board.
FAQ FAQ 
 
Noise Tolerance  Layout  Per page   
 Msgs 1 to 25 of 26 (Total in Forum: 26) (Refresh)FirstPrevNext
QuestionBorland IDE error Pinmemberloneaussie7:41 1 Aug '07  
GeneralMessage Box disappeared Pinmembersudiptam0:58 2 May '05  
Generalundefined behaviour PinsussAnonymous0:29 12 May '04  
Generalwrong virtual function called Pinmemberchrismc91222:59 25 Jan '04  
GeneralMemory leak if CreateThread gives an error Pinmemberyogieric11:35 5 Dec '03  
Generalparameter Pinsussmc_fly3:55 24 Dec '02  
Generalwhat would happen ... Pinmemberinner11:01 2 Dec '02  
Generalno need for static member function PinmemberSilvio Iaccarino3:17 31 Jul '01  
GeneralRe: no need for static member function PinmemberTomasz Sowinski4:20 31 Jul '01  
GeneralRe: no need for static member function PinmemberAnonymous4:38 31 Jul '01  
GeneralRe: no need for static member function Pinmembersilvio@iaccarino.de15:02 31 Jul '01  
GeneralCreateThread requires an ID pointer in Win9x PinmemberLee Lorenz15:12 28 Jul '01  
GeneralUse _beginthreadex PinmemberDaniel Kopitchinski1:35 28 Jul '01  
GeneralRe: Use _beginthreadex PinmemberLee Lorenz15:09 28 Jul '01  
GeneralRe: Use _beginthreadex PinmemberMichael Dunn16:25 28 Jul '01  
GeneralRe: Use _beginthreadex PinmemberLee Lorenz7:29 29 Jul '01  
GeneralRe: Use _beginthreadex PinmemberDaniel Lohmann11:39 29 Jul '01  
GeneralRe: Use _beginthreadex - EXCEPT... when using MFC PinmemberLee Lorenz14:10 29 Jul '01  
GeneralRe: Use _beginthreadex - EXCEPT... when using MFC PinmemberMichael Dunn15:48 29 Jul '01  
GeneralGood start, but needs some work PinmemberJason Douglas3:44 27 Jul '01  
GeneralRe: Good start, but needs some work PinmemberZoltan Csizmadia5:06 27 Jul '01  
GeneralRe: Good start, but needs some work PinmemberJason Douglas12:26 27 Jul '01  
GeneralRe: Good start, but needs some work PinmemberZoltan Csizmadia13:14 27 Jul '01  
GeneralRe: Good start, but needs some work PinmemberPaolo Messina14:42 27 Jul '01  
GeneralRe: Good start, but needs some work PinsussPtadeep19:01 20 Mar '03  

General General    News News    Question Question    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

PermaLink | Privacy | Terms of Use
Last Updated: 26 Jul 2001
Editor: Chris Maunder
Copyright 2001 by Ciprian Miclaus
Everything else Copyright © CodeProject, 1999-2009
Web18 | Advertise on the Code Project