*********** Member Threads *************
Introduction
It is often necessary, at least for the sake of clarity, to have a
worker thread member of a class.
Of course this is not possible, because member functions have a
hidden parameter (the "this
" pointer).
A very easy workaround consists in declaring a static function that
receives as a parameter the "this
" pointer. After a casting, the proper
member function can be called.
class CDemo
{
public:
static DWORD WINAPI ThreadStub(LPVOID Parameter)
{
((CDemo*)Parameter)->Thread();
}
void Thread()
{
for(int i=0;i<100;i++)
{
cout<<"Worker Thread: iteration n. "<<i<<endl;
}
}
};
int main(int argc, char* argv[])
{
CDemo c;
DWORD Dummy;
HANDLE h=CreateThread(NULL,NULL,CDemo::ThreadStub,&c,NULL,&Dummy);
WaitForSingleObject(h,INFINITE);
return 0;
}
I find it, at the very least, a little awkward.
Moreover, very often The first thing the Worker thread does is to fire
an event. The father thread will be waiting for this event to make sure that
the child has been actually born.
To avoid all this hassle, I created a very simple templated function, CreateMemberThread
, that does all this work for us.
Following the same sample as above, we would replace the code in the
main()
function like this:
#include "memberthreads.h"
class CDemo
{
....
....
}
int main(int argc, char* argv[])
{
CDemo c;
HANDLE h=CreateMemberThread<CDemo>(&c,CDemo::Thread);
WaitForSingleObject(h,INFINITE);
return 0;
}
I find it nice that the
CDemo
class in the example must not be derived by
another class, and that the syntax is very reminiscent of the original Windows API syntax.
One other interesting feature, is that the name of the member function
is completely arbitrary, and that more than one member thread function
can be defined in a single class.
Let's go to the implementation:
The structure ThreadData<>
holds a pointer to the instance of the class,
a pointer to the member function, and a handle to an event to signal
the start of the thread.
template <class T>
struct ThreadData
{
public:
typedef void (T::*TFunc)();
HANDLE hEvent;
T* pThreadObject;
TFunc pThreadFunc;
static DWORD _ThreadFunc(ThreadData<T>* pThis)
{
ThreadData<T> td=*pThis;
SetEvent(td.hEvent);
((*(td.pThreadObject)).*(td.pThreadFunc))();
return 0;
}
};
The function
CreateMemberThread<>
allocates on the stack a
ThreadData<>
structure, fills it, create an event, spawns the thread and waits for
the event to be fired.
That's it.
template <class T>
HANDLE CreateMemberThread(T* p,void (T::*func)())
{
ThreadData<T> td;
td.pThreadObject=p;
td.pThreadFunc=func;
td.hEvent=CreateEvent(NULL,0,0,NULL);
DWORD Dummy;
HANDLE ThreadHandle=CreateThread(NULL,NULL,
(LPTHREAD_START_ROUTINE)ThreadData<T>::_ThreadFunc,
&td,NULL,&Dummy);
WaitForSingleObject(td.hEvent,INFINITE);
::CloseHandle(td.hEvent);
return ThreadHandle;
}
Of course, nothing new has been invented, but I think that this could be
an elegant way of solving a trivial problem.
No MFC is needed, no giant #includes
(apart from a windows.h in your stdafx.h!)
Enjoy it.