Contents:
- 1 Thread Basics
- 2 When to Create a Thread
- 3 When Not to Create a Thread
- 4 Writing Your First Thread Function
- 5 The CreateThread Function
2 When to Create a Thread #
3 When Not to Create a Thread #
4 Writing Your First Thread Function #
DWORD WINAPI ThreadFunc( PVOID pvParam )
{
DWORD dwResult = 0;
...
return ( dwResult );
}
5 The CreateThread Function #
HANDLE CreateThread(
PSECURITY_ATTRIBUTES psa,
DWORD cbStack,
PTHREAD_START_ROUTINE pfnStartAddr,
PVOID pvParam,
DWORD fdwCreate,
PDWORD pdwThreadID
);
/STACK:[reserve] [,commit]
DWORD WINAPI FirstThread( PVOID pvParam )
{
// Initialize a stack-based variable
int x = 0;
DWORD dwThreadID;
// Create a new thread.
HANDLE hThread = CreateThread( NULL, 0, SecondThread, (PVOID) &x,
0, &dwThreadId );
// We don't reference the new thread anymore,
// so close our handle to it.
CloseHandle( hThread );
// Our thread is done.
// BUG: our stack will be destroyed, but
// SecondThread might try to access it.
return ( 0 );
}
DWORD WINAPI SecondThread( PVOID pvParam )
{
// Do some lengthy processing here.
...
// Attempt to access the variable on FirstThread's stack.
// NOTE: This may cause an access violation _ it depends on timing!
* ((int *) pvParam) = 5;
...
return ( 0 );
}
VOID ExitThread( DWORD dwExitCode );
BOOL TerminateThread(
HANDLE hThread,
DWORD dwExitCode
);
BOOL GetExitCodeThread(
HANDLE hThread,
PDWORD pdwExitCode
);
7 Some Thread Internals #
Figure 1. How a thread is created and initialized
VOID BaseThreadStart( PTHREAD_START_ROUTINE pfnStartAddr, PVOID pvParam )
{
__try {
ExitThread( (pfnStartAddr)(pvParam) );
}
_ _except( UnhandledExceptionFilter( GetExceptionInformation() ) ) {
ExitProcess( GetExceptionCode() );
}
// NOTE: We never get here.
}
VOID BaseProcessStart( PPROCESS_START_ROUTINE pfnStartAddr )
{
__try {
ExitThread( (pfnStartAddr)() );
}
__except( UnhandledExceptionFilter( GetExceptionInformation() ) ) {
ExitProcess( GetExceptionCode() );
}
// NOTE: We never get here.
}
8 C/C++ Run-Time Library Considerations #
| Library Name | Description |
| LibC.lib | Statically linked library for single-threaded applications. (This is the default library when you create a new project.) |
| LibCD.lib | Statically linked debug version of the library for single-threaded applications. |
| LibCMt.lib | Statically linked release version of the library for multithreaded applications. |
| LibCMtD.lib | Statically linked debug version of the library for multithreaded applications. |
| MSVCRt.lib | Import library for dynamically linking the release version of the MSVCRt.dll library. This library supports both single-threaded and multithreaded applications. |
| MSVCRtD.lib | Import library for dynamically linking the debug version of the MSVCRtD.dll library. The library supports both single-threaded and multithreaded applications. |
BOOL fFailure = ( system( "NOTEPAD.EXE README.TXT" ) == -1 );
if( fFailure )
{
switch( errno ) {
case E2BIG: // Argument list or environment too big
break;
case ENOENT: // Command interpreter cannot be found
break;
case ENOEXEC: // Command interpreter has bad format
break;
case ENOMEM: // Insufficient memory to run command
break;
}
}
unsigned long _beginthreadex(
void *security,
unsigned stack_size,
unsigned (*start_address)(void *),
void *arglist,
unsigned initflag,
unsigned *thrdaddr
);
typedef unsigned (__stdcall *PTHREAD_START) (void *);
#define chBEGINTHREADEX( psa, cbStack, pfnStartAddr, \
pvParam, fdwCreate, pdwThreadID ) \
( (HANDLE) _beginthreadex( \
(void *) (psa), \
(unsigned) (cbStack), \
(PTHREAD_START) (pfnStartAddr), \
(void *) (pvParam), \
(unsigned) (fdwCreate), \
(unsigned *) (pdwThreadID)) )
unsigned long __cdecl _beginthreadex(
void *psa,
unsigned cbStack,
unsigned (__stdcall * pfnStartAddr) (void *),
void * pvParam,
unsigned fdwCreate,
unsigned *pdwThreadID )
{
_ptiddata ptd; // Pointer to thread's data block
unsigned long thdl; // Thread's handle
// Allocate data block for the new thread.
if( ( ptd = _calloc_crt( 1, sizeof( struct tiddata ) ) ) == NULL )
goto error_return;
// Initialize the data block.
initptd( ptd );
// Save the desired thread function and the parameter
// we want it to get in the data block.
ptd->_initaddr = (void *) pfnStartAddr;
ptd->_initarg = pvParam;
// Create the new thread.
thdl = (unsigned long) CreateThread( psa, cbStack,
_threadstartex, (PVOID) ptd, fdwCreate, pdwThreadID );
if( thdl == NULL )
{
// Thread couldn't be created, cleanup and return failure.
goto error_return;
}
// Create created OK, return the handle.
return ( thdl );
error_return:
// Error: data block or thread couldn't be created.
_free_crt( ptd );
return ( (unsigned long) 0L );
}
struct _tiddata {
unsigned long _tid; /* thread ID */
unsigned long _thandle; /* thread handle */
int _terrno; /* errno value */
unsigned long _tdoserrno; /* _doserrno value */
unsigned int _fpds; /* Floating Point data segment */
unsigned long _holdrand; /* rand() seed value */
char * _token; /* ptr to strtok() token */
#ifdef _WIN32
wchar_t * _wtoken; /* ptr to wcstok() token */
#endif /* _WIN32 */
unsigned char * _mtoken; /* ptr to _mbstok() token */
/* following pointers get malloc'd at runtime */
char * _errmsg; /* ptr to strerror()/_strerror() buff */
char * _namebuf0; /* ptr to tmpnam() buffer */
#ifdef _WIN32
wchar_t * _wnamebuf0; /* ptr to _wtmpnam() buffer */
#endif /* _WIN32 */
char * _namebuf1; /* ptr to tmpfile() buffer */
#ifdef _WIN32
wchar_t * _wnamebuf1; /* ptr to _wtmpfile() buffer */
#endif /* _WIN32 */
char * _asctimebuf; /* ptr to asctime() buffer */
#ifdef _WIN32
wchar_t * _wasctimebuf; /* ptr to _wasctime() buffer */
#endif /* _WIN32 */
void * _gmtimebuf; /* ptr to gmtime() structure */
char * _cvtbuf; /* ptr to ecvt()/fcvt buffer */
/* following fields are needed by _beginthread code */
void * _initaddr; /* initial user thread address */
void * _initarg; /* initial user thread argument */
/* following three fields are needed to support signal handling and
* runtime errors */
void * _pxcptacttab; /* ptr to exception-action table */
void * _tpxcptinfoptrs; /* ptr to exception info pointers */
int _tfpecode; /* float point exception code */
/* following field is needed by NLG routines */
unsigned long _NLG_dwCode;
/*
* Per-Thread data needed by C++ Exception Handling
*/
void * _terminate; /* terminate() routine */
void * _unexpected; /* unexpected() routine */
void * _translator; /* S.E. translator */
void * _curexception; /* current exception */
void * _curcontext; /* current exception context */
#if defined (_M_MRX000)
void * _pFrameInfoChain;
void * _pUnwindContext;
void * _pExitContext;
int _MipsPtdDelta;
int _MipsPtdEpsilon;
#elif defined (_M_PPC)
void * _pExitContext;
void * _pUnwindContext;
void * _pFrameInfoChain;
int _FrameInfo[6];
#endif /* defined (_M_PPC) */
};
typedef struct _tiddata * _ptiddata;
static unsigned long WINAPI threadstartex ( void* ptd )
{
// Note: ptd is the address of this thread's tiddata block.
// Associate the tiddata block with this thread.
TlsSetValue( __tlsindex, ptd );
// Save this thread ID in the tiddata block.
( (_ptiddata) ptd )->_tid = GetCurrentThreadId();
// Initialize floating-point support (code not shown).
// Wrap desired thread function in SEH frame to
// handle run-time errors and signal support.
__try {
// Call desired thread function, passing it the desired parameter.
// Pass thread's exit code value to _endthreadex.
_endthreadex(
( (unsigned (WINAPI *)(void *) )( ((_ptiddata)ptd)->_initaddr ) )
( ((_ptiddata)ptd)->_initarg ) ) ;
}
__except( _XcptFilter( GetExceptionCode(), GetExceptionInformation() ) ) {
// The C run-time's exception handler deals with run-time errors
// and signal support; we should never get it here.
_exit( GetExceptionCode() );
}
// We never get here; the thread dies in this function.
return ( 0L );
}
void __cdecl _endthreadex( unsigned retcode )
{
_ptiddata ptd; // Pointer to thread's data block
// Clean up floating-point support (code not shown).
// Get the address of this thread's tiddata block.
ptd = _getptd();
// Free the tiddata block.
_freeptd( ptd );
// Terminate the thread.
ExitThread( retcode );
}
#if defined( _MT ) || defined( _DLL )
extern int * __cdecl _errno( void );
#define errno ( *_errno() )
#else /* ndef _MT && ndef _DLL */
extern int errno;
#endif /* _MT || _DLL */
int *p = &errno;
if ( *p == ENOMEM ) {
...
}
unsigned long _beginthread(
void (__cdecl *start_address)(void *),
unsigned stack_size,
void *arglist
);
DWORD dwExitCode;
HANDLE hThread = _beginthread( ... );
GetExitCodeThread( hThread, &dwExitCode );
CloseHandle( hThread );
9 Gaining a Sense of One's Own Identity #
HANDLE GetCurrentProcess();
HANDLE GetCurrentThread();
FILETIME ftCreationTime, ftExitTime, ftKernelTime, ftUserTime;
GetProcessTimes( GetCurrentProcess(),
&ftCreationTime, &ftExitTime, &ftKernelTime, &ftUserTime );
FILETIME ftCreationTime, ftExitTime, ftKernelTime, ftUserTime;
GetThreadTimes( GetCurrentThread(),
&ftCreationTime, &ftExitTime, &ftKernelTime, &ftUserTime );
DWORD GetCurrentProcessId();
DWORD GetCurrentThreadId();
DWORD WINAPI ParentThread( PVOID pvParam ) {
HANDLE hThreadParent = GetCurrentThread();
CreateThread( NULL, 0, ChildThread, (PVOID) hThreadParent, 0, NULL );
// Function continues...
}
DWORD WINAPI ChildThread( PVOID pvParam ) {
HANDLE hThreadParent = (HANDLE) pvParam;
FILETIME ftCreationTime, ftExitTime, ftKernelTime, ftUserTime;
GetThreadTimes( hThreadParent,
&ftCreationTime, &ftExitTime, &ftKernelTime, &ftUserTime );
// Function continues...
}
BOOL DuplicateHandle(
HANDLE hSourceProcess,
HANDLE hSource,
HANDLE hTargetProcess,
PHANDLE phTarget,
DWORD fdwAccess,
BOOL bInheritHandle,
DWORD fdwOptions
);
DWORD WINAPI ParentThread( PVOID pvParam ) {
HANDLE hThreadParent;
DuplicateHandle(
GetCurrentProcess(), // Handle of process that thread
// pseudo-handle is relative to
GetCurrentThread(), // Parent thread's pseudo-handle
GetCurrentProcess(), // Handle of process that the new, real,
// thread handle is relative to
&hThreadParent, // Will receive the new, real, handle
// identifying the parent thread
0, // Ignored due to DUPLICATE_SAME_ACCESS
FALSE, // New thread handle is not inheritable
DUPLICATE_SAME_ACCESS); // New thread handle has same
// access as pseudo-handle
CreateThread( NULL, 0, ChildThread, (PVOID) hThreadParent, 0, NULL );
// Function continues...
}
DWORD WINAPI ChildThread( PVOID pvParam ) {
HANDLE hThreadParent = (HANDLE) pvParam;
FILETIME ftCreationTime, ftExitTime, ftKernelTime, ftUserTime;
GetThreadTimes( hThreadParent,
&ftCreationTime, &ftExitTime, &ftKernelTime, &ftUserTime );
CloseHandle( hThreadParent );
// Function continues...
}
HANDLE hProcess;
DuplicateHandle(
GetCurrentProcess(), // Handle of process that the process
// pseudo-handle is relative to
GetCurrentProcess(), // Process's pseudo-handle
GetCurrentProcess(), // Handle of process that the new, real,
// process handle is relative to
&hProcess, // Will receive the new, real
// handle identifying the process
0, // Ignored because of DUPLICATE_SAME_ACCESS
FALSE, // New thread handle is not inheritable
DUPLICATE_SAME_ACCESS // New process handle has same
); // access as pseudo-handle
CategoryTranslation