Á¤¸®> Thread Basics
 FrontPage|FindPage|TitleIndex|RecentChanges|UserPreferences U E D R S P I M H RSS

ÀÓ½Ã/SubCategoryCppÀ⵿»ç´ÏÁ¤¸®Á¤¸®/°øºÎ¸Þ¸ðÀåÁ¤¸®/KernelObjects › Á¤¸®/ThreadBasics
<!> UnderConstruction

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


1 Thread Basics #



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 );
}

6 Terminating a Thread #


VOID ExitThread( DWORD dwExitCode );

BOOL TerminateThread(
	HANDLE hThread,
	DWORD dwExitCode
);

BOOL GetExitCodeThread(
	HANDLE hThread,
	PDWORD pdwExitCode
);

7 Some Thread Internals #


Programming/Thread/ThreadKernelObject.gif
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 NameDescription
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.

Programming/Thread/MultiThread.gif

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
);

void _endthread( void );

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

EditText|FindPage|DeletePage|LikePages| Valid XHTML 1.0! Valid CSS! powered by MoniWiki