关于_beginthreadex与_endthreadex的问题

allen_zhaozhencn 2004-07-19 03:29:32
关于_beginthreadex与_endthreadex的问题

在线程函数中,要不要调用_endthreadex来释放堆中内存空间及ExitThread?

如果调用该函数的话,在线程函数中的局部对象将不能得到正常的析构.如果不调用这个函数,无法释放与线程相关的_tiddata结构体. 请高手指点.
...全文
1271 15 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
15 条回复
切换为时间正序
请发表友善的回复…
发表回复
shadowWind 2005-02-17
  • 打赏
  • 举报
回复
mark
allen_zhaozhencn 2004-07-20
  • 打赏
  • 举报
回复
我明白了,谢谢各位.稍侯给分
sevencat 2004-07-20
  • 打赏
  • 举报
回复
<windows 核心编程>里面说得比较清楚的。看一下就知道了。
会思考的草 2004-07-20
  • 打赏
  • 举报
回复
详细的解释你还是去看看MSJ July 1999的Win32Q&A。
会思考的草 2004-07-20
  • 打赏
  • 举报
回复
Jeffrey Richter 给出的一些注解:
first, if you call _beginthreadex, you'll get back a handle to the thread. At some point, that thread's handle must be closed. _endthreadex **doesn't** do it. Normally, the thread that called _beginthreadex (possibly the main thread) will call CloseHandle on the newly created thread's handle when the thread handle is no longer needed. Second, you only need to use _beginthreadex if your app uses CRT functions. If it doesn't, then you can just use CreateThread. Also, you can use CreateThread if only one thread (the main thread) in your app uses the CRT. If newly created threads don't use the CRT, you don't need _beginthreadex or the multithreaded CRT.
会思考的草 2004-07-20
  • 打赏
  • 举报
回复
看看它们的代码就知道了(以下代码摘自MSJ1999):
_beginthreadex 伪代码:
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 = calloccrt(1, sizeof(struct tiddata))) == NULL)
goto errorreturn;

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


void __cdecl _endthreadex (unsigned retcode) {
_ptiddata ptd; // Pointer to thread's data block

// Cleanup 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);
}
sevencat 2004-07-19
  • 打赏
  • 举报
回复
以createthread中传入的函数入口地址并不你的函数,而是他自己的一个。
CreateThread( security,
stacksize,
_threadstartex,
(LPVOID)ptd,
createflag,
thrdaddr))

sevencat 2004-07-19
  • 打赏
  • 举报
回复
先贴原代码
uintptr_t __cdecl _beginthreadex (
void *security,
unsigned stacksize,
unsigned (__stdcall * initialcode) (void *),
void * argument,
unsigned createflag,
unsigned *thrdaddr
)
{
_ptiddata ptd; /* pointer to per-thread data */
uintptr_t thdl; /* thread handle */
unsigned long errcode = 0L; /* Return from GetLastError() */
unsigned dummyid; /* dummy returned thread ID */

if ( initialcode == NULL ) {
errno = EINVAL;
return( (uintptr_t)0 );
}

/*
* Allocate and initialize a per-thread data structure for the to-
* be-created thread.
*/
if ( (ptd = _calloc_crt(1, sizeof(struct _tiddata))) == NULL )
goto error_return;

/*
* Initialize the per-thread data
*/

_initptd(ptd);

ptd->_initaddr = (void *) initialcode;
ptd->_initarg = argument;
ptd->_thandle = (uintptr_t)(-1);

/*
* Make sure non-NULL thrdaddr is passed to CreateThread
*/
if ( thrdaddr == NULL )
thrdaddr = &dummyid;

/*
* Create the new thread using the parameters supplied by the caller.
*/
if ( (thdl = (uintptr_t)
CreateThread( security,
stacksize,
_threadstartex,
(LPVOID)ptd,
createflag,
thrdaddr))
== (uintptr_t)0 )
{
errcode = GetLastError();
goto error_return;
}

/*
* Good return
*/
return(thdl);

/*
* Error return
*/
error_return:
/*
* Either ptd is NULL, or it points to the no-longer-necessary block
* calloc-ed for the _tiddata struct which should now be freed up.
*/
_free_crt(ptd);

/*
* Map the error, if necessary.
*
* Note: this routine returns 0 for failure, just like the Win32
* API CreateThread, but _beginthread() returns -1 for failure.
*/
if ( errcode != 0L )
_dosmaperr(errcode);

return( (uintptr_t)0 );
}


/***
*_threadstartex() - New thread begins here
*
*Purpose:
* The new thread begins execution here. This routine, in turn,
* passes control to the user's code.
*
*Entry:
* void *ptd = pointer to _tiddata structure for this thread
*
*Exit:
* Never returns - terminates thread!
*
*Exceptions:
*
*******************************************************************************/

static unsigned long WINAPI _threadstartex (
void * ptd
)
{
_ptiddata _ptd; /* pointer to per-thread data */

/*
* Check if ptd is initialised during THREAD_ATTACH call to dll mains
*/
if ( ( _ptd = FLS_GETVALUE(__tlsindex)) == NULL)
{
/*
* Stash the pointer to the per-thread data stucture in TLS
*/
if ( !FLS_SETVALUE(__tlsindex, ptd) )
_amsg_exit(_RT_THREAD);
/*
* Set the thread ID field -- parent thread cannot set it after
* CreateThread() returns since the child thread might have run
* to completion and already freed its per-thread data block!
*/
((_ptiddata) ptd)->_tid = GetCurrentThreadId();
}
else
{
_ptd->_initaddr = ((_ptiddata) ptd)->_initaddr;
_ptd->_initarg = ((_ptiddata) ptd)->_initarg;
_free_crt(ptd);
ptd = _ptd;
}


/*
* Call fp initialization, if necessary
*/
if ( _FPmtinit != NULL )
(*_FPmtinit)();

/*
* Guard call to user code with a _try - _except statement to
* implement runtime errors and signal support
*/
__try {
_endthreadex (
( (unsigned (WINAPI *)(void *))(((_ptiddata)ptd)->_initaddr) )
( ((_ptiddata)ptd)->_initarg ) ) ;
}
__except ( _XcptFilter(GetExceptionCode(), GetExceptionInformation()) )
{
/*
* Should never reach here
*/
_exit( GetExceptionCode() );

} /* end of _try - _except */

/*
* Never executed!
*/
return(0L);
}


/***
*_endthreadex() - Terminate the calling thread
*
*Purpose:
*
*Entry:
* Thread exit code
*
*Exit:
* Never returns!
*
*Exceptions:
*
*******************************************************************************/

void __cdecl _endthreadex (
unsigned retcode
)
{
_ptiddata ptd; /* pointer to thread's _tiddata struct */

/*
* Call fp termination, if necessary
*/
if ( _FPmtterm != NULL )
(*_FPmtterm)();

if ( (ptd = _getptd()) == NULL )
_amsg_exit(_RT_THREAD);

/*
* Free up the _tiddata structure & its subordinate buffers
* _freeptd() will also clear the value for this thread
* of the FLS variable __tlsindex.
*/
_freeptd(ptd);

/*
* Terminate the thread
*/
ExitThread(retcode);

}
lianglp 2004-07-19
  • 打赏
  • 举报
回复
只要用_beginthXXX()函数创建的线程,_tddata结构会有保证得到释放的。
因为用_beginthXXX()创建的时候,它本身会创建一个线程处理过程,然后在
这个线程处理过程中调用用户传递的线程函数而已,
就比如:

DWORD _stdcall RawThreadProc(DWORD dwParam)//为原始线程处理过程
{
ThreadParam* ptheadparam = (ThreadParam*)dwParam ;
_tddata* ptpdata = new _tddata ;
ptpdata->XXX = ... ;
...//在线程中初始化值 ;
DWORD dwResult = ptheadparam.pfnThreadProc(pthreadparam->dwParam) ;//这个才是用户真正的线程处理过程
delete _ptpdata ;
_endthreadex(dwResult) ;
}


DWORD _stdcall _beginthreadEx(...,userThreadProc,userParam,...) ;//简单说明线程函数是怎样创建的
{
...
ThreadParam threadParam ;//ThreadParam为一个内部传递到线程中的一个函数。
threadParam.pfthreadproc = userThreadProc ;
threadParam.userParam = userParam ;
...
HANDLE hThread = ::CreateThread(...,RawThreadProc,...) ;//调用win api创建线程
...
return (DWORD)hThread ;
}

以上是一个_beginthreadEx()函数的实现原理,真正_beginthreadEx()实现不是这样的。
vcforever 2004-07-19
  • 打赏
  • 举报
回复
在程序正常return的情况下,即使不调用_endthreadex函数,_tddata结构也会被释放.

如果要是强制结束线程的话,可以显式的调用_endthreadex函数来释放_tddata结构!
allen_zhaozhencn 2004-07-19
  • 打赏
  • 举报
回复
谢谢楼上各位的回答.
weirdy说, beginthreadex会自动调用_endthreadex ,我看了代码, 它只是__createthreadex错误返回时才释放,我的问题是从线程函数正常return情况下.不显式调用__endthreadex,这个_tiddata是否会释放,如果不释放,又该怎么办?
如下:
* Error return
*/
error_return:
/*
* Either ptd is NULL, or it points to the no-longer-necessary block
* calloc-ed for the _tiddata struct which should now be freed up.
*/
_free_crt(ptd);

/*
* Map the error, if necessary.
*
* Note: this routine returns 0 for failure, just like the Win32
* API CreateThread, but _beginthread() returns -1 for failure.
*/
if ( errcode != 0L )
_dosmaperr(errcode);

return( (uintptr_t)0 );
vcforever 2004-07-19
  • 打赏
  • 举报
回复
最好在线程函数中不要调用_endthreadex()函数,
最安全的方式是让线程函数自然的return,如果在线程函数中调用了_endthreadex函数的话,线程就会在调用_endthreadex函数调用的地方立即退出,不回继续向下执行,导致在线程局部存储空间中的局部对象或者变量的析构函数不会被调用,有可能导致资源的泄漏!

_tiddata结构体
是记录线程中用到的局部数据的一个数据结构,可以在mtdll.h文件中找到其声明!
sevencat 2004-07-19
  • 打赏
  • 举报
回复
agree weirdy,看原代码就知道了。会自动调用。
xenke 2004-07-19
  • 打赏
  • 举报
回复
_beginthreadex会自动调用_endthreadex
clane 2004-07-19
  • 打赏
  • 举报
回复
只要直接return出来就可以了,当然exitthread效果一样。呵呵

15,473

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC 进程/线程/DLL
社区管理员
  • 进程/线程/DLL社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧