|
|
|||
|
|
|||
|
| |||
Using _beginthreadex
The classic method of creating Win32 threads is to use
HANDLE hThread = NULL;
DWORD myThread( DWORD arg )
{
// do something
_endthreadex(0);
return 0;
}
void StartThread( void )
{
SECURITY_ATTRIBUTES sa;
DWORD threadId = 0;
DWORD parm = 0;
sa.nLength = sizeof( sa );
sa.lpSecurityDescriptor = NULL;
hThread = (HANDLE)_beginthreadex( (void*)&sa, 0,
(unsigned (__stdcall *) (void *)) &myThread,
(void*) &parm, 0, (unsigned int *)&threadId );
}
This works pretty well, and is a clean, non-MFC way of doing it. Even in an MFC application, it will work. Let's say you wanted to use a member of your CDoc as the thread function. This member can take any number (or no) parameters, and can be influenced by other CDoc members. You can do it with a wrapper function, passing this in as your parameter, like this:
DWORD CDoc::myThreadStub( DWORD arg )
{
return ((CDoc*)arg)->myThread();
}
DWORD CDoc::myThread( void )
{
// do something
_endthreadex(0);
return 0;
}
void CDoc::StartThread( void )
{
SECURITY_ATTRIBUTES sa;
DWORD threadId = 0;
sa.nLength = sizeof( sa );
sa.lpSecurityDescriptor = NULL;
// assumes hThread is a member of CDoc
hThread = (HANDLE)_beginthreadex( (void*)&sa, 0,
(unsigned (__stdcall *) (void *)) &myThreadStub,
(void*)this, 0, (unsigned int *)&threadId );
}
Everything is working fine. You make your thread fancier, popping up a
system modal message box using
CMyFancyDialog dlg;
if( IDOK == dlg.DoModal() )
{
// do something fancy
}
Except it doesn't work. Maybe while running the release build, you get a
GPF instead of your dialog. Or, if you a running in debug mode, you get an
assert failure in
// Note: if either of the above asserts fire and you are // writing a multithreaded application, it is likely that // you have passed a C++ object from one thread to another // and have used that object in a way that was not intended. // (only simple inline wrapper functions should be used) // // In general, CWnd objects should be passed by HWND from // one thread to another. The receiving thread can wrap // the HWND with a CWnd object by using CWnd::FromHandle. // // It is dangerous to pass C++ objects from one thread to // another, unless the objects are designed to be used in // such a manner.Okay, you feel a little better... enough people have run into this problem that there were comments in the MFC source. But what it doesn't seem to apply... you are running a multithreaded application, but you weren't passing CWnd objects. Do some searching on
The assert that is failing is in the debug-mode function
|
|
|||
|
|
|||
|
| |||
DWORD CDoc::myThreadStub( DWORD arg )
{
return ((CDoc*)arg)->myThread();
}
DWORD CDoc::myThread( void )
{
// do something
AfxEndThread(0);
return 0;
}
void CDoc::StartThread( void )
{
// assume pThread is a CWinThread* member of CDoc
pThread = AfxBeginThread( (AFX_THREADPROC) &myThreadStub, (void *)this );
}
A lot cleaner, and a lot safer, too.
|
|
|||
|
|
|||
|
| |||
DWORD CDoc::myThread( void )
{
// assumes bStopThread is bool member of CDoc
while( !bStopThread )
{
// do something
::Sleep( 100 );
}
AfxEndThread(0);
return 0;
}
Then in your code, when you want to shut down the thread, you simply
set the value true, and wait.
|
|
|||
|
|
|||
|
| |||
DWORD CDoc::myThread( void )
{
HRESULT hRes = CoInitializeEx( NULL, COINIT_MULTITHREADED );
if ( FAILED( hRes ) )
{
AfxMessageBox( "CoInitializeEx() Failed for myThread!", MB_OK | MB_ICONSTOP, 0 );
AfxEndThread(1);
return 1;
}
// assumes bStopThread is bool member of CDoc
while( !bStopThread )
{
// do something
::Sleep( 100 );
}
CoUninitialize();
AfxEndThread(0);
return 0;
}
|
These pages provided exclusively by
apostate.com. Unless otherwise noted,
all text Copyright 1994-1999 by
Joshua J. Ellis (josh@apostate.com).
All rights reserved. Use of the information hereon is subject to the terms
of Legal Statement. Celtic artwork courtesy
of Karen Nicholas. /programming/mfc-threads.html last modified Mon, 31-Jul-2000 |