You generally create a thread using
CreateThread
or
AfxBeginThread
, and pass a function which
must match the prototype as laid down by
thread-creation function.
For example:
DWORD __stdcall TheThread(void* pParam)
{
return 0;
}
int _tmain(int argc, _TCHAR* argv[])
{
DWORD dwTID;
CreateThread(NULL, 0,TheThread, NULL,0, &dwTID);
return 0;
}
For now, let's assume the argument to thread is not required.
Wouldn't it be better if you create the thread by just declaring the thread-function that takes
void
and returns
void
, and have a delegator that would actually be
LPTHREAD_START_ROUTINE
?
Okay, let me exemplify with an example. Wouldn't this suit you?
void ThreadRoutine()
{
}
int main()
{
DelegateCreateThread(ThreadRoutine);
}
The thread-creation
delegator would be responsible for creating the thread, and it would schedule the specified function to be acted as
thread.
Now, let's see how we can link them together. This might seem slightly complicated, but can be understood easily!
First, let
typedef
a function-pointer that takes
void
and returns
void
:
typedef void (*Pointer2FunctionVV)();</br>
Second, have your thread routine:
void ThreadRoutine()
{
}
Thirdly, let me show you the actual delegator!
DWORD __stdcall ThreadDelegator(void* pThread)
{
Pointer2FunctionVV Func;
Func = (Pointer2FunctionVV)pThread;
Func();
return 0;
}
The delegator function is put in four lines, for simplicity. Depending on your taste, coding standards and flexibility level, you can reduce the call as short as:
((Pointer2FunctionVV)pThread)();
return 0;
And finally, the
DelegateCreateThread
:
void DelegateCreateThread( Pointer2FunctionVV pThread )
{
DWORD dwTID;
CreateThread(NULL,0, ThreadDelegator, pThread, 0, &dwTID);
}
And if you dont like this as function, you can #define it as a macro too:
#define DelegateCreateThread(func) { \
DWORD dwTID; \
CreateThread(NULL,0, ThreadDelegator, (func), 0, &dwTID); \
}
Now, you just need to call
DelegateCreateThread
, and pass your
void(void)
style function that should be executed as a thread.
What if you need to pass an argument to your thread function?
Well, this demands some attention and interest from you. This approach will be used with
AfxBeginThread
later.
First, let's have our function
typedef
'ed:
typedef void (*Pointer2FunctionVP)(void*);
If you are understanding it carefully, you are aware that the
'param' parameter of
CreateThread
is already utilized to pass actual function. Therefore, we need to use following approach, which uses our custom
struct
to pass data.
(Second,) We declare
ThreadDelegatorData
structure:
struct ThreadDelegatorData
{
Pointer2FunctionVP ThreadFunc;
void* Param;
};
Thirdly, we code another thread-delegator function, which initializes this structure (on heap), and passes it to
CreateThread
:
void DelegateCreateThread(Pointer2FunctionVP pThread, void* pParam)
{
ThreadDelegatorData* pDelegatorData = new ThreadDelegatorData();
pDelegatorData->Param = pParam;
pDelegatorData->ThreadFunc = pThread;<br>
DWORD dwTID;
CreateThread(NULL, 0, ThreadDelegatorWithParam, pDelegatorData, 0, &dwTID);
}</br>
I assume you know why this object is allocated on heap, instead of stack.
Finally, in our thread-function (
ThreadDelegatorWithParam
) we call the function which is specified by user:
DWORD __stdcall ThreadDelegatorWithParam(void* pParam)
{
ThreadDelegatorData* pDelegatorData = (ThreadDelegatorData*)pParam;
(pDelegatorData->ThreadFunc)(pDelegatorData->Param);
delete pDelegatorData;
return 0;
}
You can use the same approach with
AfxBeginThread
(or any other thread-creation function), you just need to keep the thread-delegator match with thread-creator's desired prototype.
What if you need to delegate a member-function of a class to be classified as the thread?
First let me enlighten this topic. The following example (
simulation) has a
CDialog
inherited class, it starts a thread when user asks for processing. The thread-function does the processing, and regularly sends (posts) the progress updates (via
PostMessage
). Note that this is a simulation, thus only relevant code is given.
class CMyDialog
{
int Count;
public:
void OnOK()
{
Count = 0;
AfxBeginThread(ProcessingThread, this);
}
static UINT ProcessingThread(void* pParam)
{
CMyDialog *pDialog = (CMyDialog*)pParam;
for (int nIter = 1; nIter <= 100; nIter++)
{
pDialog->Count += nIter;
}
return 0;
}
};
You can avoid cumbersome and unreadable '
pDialog->
' stuff by:
static UINT ProcessingThread(void* pParam)
{
CMyDialog *pDialog = (CMyDialog*)pParam;
pDialog->ProcessingThreadProc();
return 0;
}
void ProcessingThreadProc()
{
for (int nIter = 1; nIter <= 100; nIter++)
{
Count += nIter;
}
}
Which reduces some clutter.
Now, you would be interested in thread-delegator, that would create thread in more simple way, like:
DelegateBeginThread(ProcessingThreadProc);
Well, that's not that straightforward, and it needs some hack. I would provide a solution that would work for the code given above, but for now, let's start with simple one.
First, have a typedef for member-to-function pointer:
typedef void (CMyDialog::*Pointer2MemFunVV)();
Secondly, define the thread-delegator:
void DelegateBeginThread(Pointer2MemFunVV pmf)
{
}
(
Coming to commented part!)
Thirdly, call the thread-delegator:
DelegateBeginThread(&CMyDialog::ProcessingThreadProc);
For now, first let's assume the actual thread doesn't demand '
this
' pointer; object (CMyDialog) is accessible. Thus, we may pass the thread-function to be called to
CreateThread
or
AfxBeginThread
.
Caveat! You cannot do that! Both thread creation function takes LPVOID as
param, and by no conversion style you can covert a member-to-function pointer to void* (or any pointer); and vice versa! Doing this raises
C2440. Why? This needs a long discussion, but in short: A class may involve multiple inheritance, and that causes
sizeof
M2F pointer to be of 8 bytes or more, on 32-bit compilation. Compiler cannot be flexible enough to allow the conversion, knowing/feeling that our class doesn't involve MI - A rule is a rule! (Read
this article)
I found a hack for fooling the compiler for this. Just declare a
union
, have a void pointer and M2F pointer. Assign M2F with a function' address, and pass the void* to thread creator:
union MemFun_Param
{
Pointer2MemFunVV func;
void* param;
};
...
void DelegateBeginThread(Pointer2MemFunVV pmf)
{
MemFun_Param mp;
mp.func = pmf;
AfxBeginThread(ProcessingThread, mp.param);
}
Yes, it is important to know that size of M2F and void-pointer is same! We can use static asserts or runtime check to do that. Short of space, cannot discuss that.
And, the thread routine, which should call user specified function, can be coded as:
CMyDialog dialog;
UINT CMyDialog::ProcessingThread(void* pParam)
{
MemFun_Param mp;
mp.param = pParam;
(dialog.*mp.func)();
return 0;
}
Aww! By this time, I realize this Tip/Trick has gone much bigger than I initially thought. I suppose I should delete this, and put it as an article!