什么是更好的方式来启动一个线程_beginthread
,_beginthreadx
或CreateThread
?
我试图确定什么是优势/劣势_beginthread
,_beginthreadex
和CreateThread
.所有这些函数都返回一个新创建的线程的线程句柄,我已经知道CreateThread在发生错误时提供了一些额外的信息(可以通过调用来检查GetLastError
)......但是当我发现时,我应该考虑哪些事情?我用这些功能?
我正在使用Windows应用程序,因此跨平台兼容性已经不可能了.
我已经浏览了msdn文档,但我无法理解,例如,为什么有人会决定使用_beginthread而不是CreateThread,反之亦然.
干杯!
更新:好的,感谢所有的信息,我也读过几个我不能打电话的地方,WaitForSingleObject()
如果我用的话_beginthread()
,但是如果我_endthread()
在线程中打电话应该不行吗?这有什么交易?
CreateThread()
是一个原始的Win32 API调用,用于在内核级别创建另一个控制线程.
_beginthread()
&_beginthreadex()
是CreateThread()
在后台调用的C运行时库调用.一旦CreateThread()
返回,_beginthread/ex()
就会负责额外的簿记,以使C运行时库在新线程中可用且一致.
在C++中,您几乎肯定会使用,_beginthreadex()
除非您根本不会链接到C运行时库(也就是MSVCRT*.dll/.lib).
_beginthread()
和之间有几点不同_beginthreadex()
. _beginthreadex()
被制作成更像CreateThread()
(在两个参数和它的行为方式).
正如Drew Hall所提到的,如果您正在使用C/C++运行时,则必须使用_beginthread()
/ _beginthreadex()
而不是CreateThread()
使运行时有机会执行它自己的线程初始化(设置线程本地存储等).
在实践中,这意味着CreateThread()
您的代码几乎不会直接使用它.
对于MSDN文档_beginthread()
/ _beginthreadex()
有相当多的细节上的差异-更重要的一个是,因为通过创建一个线程的线程处理_beginthread()
得到由CRT自动关闭,当线程退出,"如果由_beginthread出口产生的线程很快,返回给_beginthread调用者的句柄可能无效,或者更糟糕的是,指向另一个线程".
这是_beginthreadex()
CRT来源的评论必须说:
Differences between _beginthread/_endthread and the "ex" versions:
1) _beginthreadex takes the 3 extra parameters to CreateThread
which are lacking in _beginthread():
A) security descriptor for the new thread
B) initial thread state (running/asleep)
C) pointer to return ID of newly created thread
2) The routine passed to _beginthread() must be __cdecl and has
no return code, but the routine passed to _beginthreadex()
must be __stdcall and returns a thread exit code. _endthread
likewise takes no parameter and calls ExitThread() with a
parameter of zero, but _endthreadex() takes a parameter as
thread exit code.
3) _endthread implicitly closes the handle to the thread, but
_endthreadex does not!
4) _beginthread returns -1 for failure, _beginthreadex returns
0 for failure (just like CreateThread).
2013年1月更新:
VS 2012的CRT还执行了一些额外的初始化_beginthreadex()
:如果进程是"打包应用程序"(如果返回了一些有用的东西GetCurrentPackageId()
),运行时将在新创建的线程上初始化MTA.
一般来说,正确的做法是调用_beginthread()/_endthread()
(或ex()
变体).但是,如果您使用CRT作为一个.dll,在CRT状态将被正确的初始化和销毁CRT的DllMain
将被用DLL_THREAD_ATTACH
和DLL_THREAD_DETACH
打电话时CreateThread()
,并ExitThread()
分别或返回.
DllMain
可以在VC\crt\src\crtlib.c下的VS的安装目录中找到CRT 的代码.
这是代码的核心_beginthreadex
(参见crt\src\threadex.c
):
/* * Create the new thread using the parameters supplied by the caller. */ if ( (thdl = (uintptr_t) CreateThread( (LPSECURITY_ATTRIBUTES)security, stacksize, _threadstartex, (LPVOID)ptd, createflag, (LPDWORD)thrdaddr)) == (uintptr_t)0 ) { err = GetLastError(); goto error_return; }
其余的_beginthreadex
初始化CRT的每线程数据结构.
使用的优点_beginthread*
是您的线程CRT调用将正常工作.
您应该使用_beginthread
或_beginthreadex
允许C运行时库自己进行线程初始化.只有C/C++程序员需要知道这一点,因为他们现在应该使用他们自己的开发环境的规则.
如果你使用,_beginthread
你不需要打电话,CloseHandle
因为RTL会为你做.这就是为什么你不能在手柄上等待的原因_beginthread
.也_beginthread
导致混乱,如果线程函数立即退出(快速)作为启动线程我可以左手拿着无效的线程句柄,它刚刚推出的线程.
_beginthreadex
句柄可以用于等待,但也需要显式调用CloseHandle
.这是使他们安全使用等待的一部分.使其完全万无一失的另一个问题是始终启动线程暂停.检查成功,记录句柄等.恢复线程.这是防止线程在启动线程可以记录其句柄之前终止所必需的.
最佳做法是使用_beginthreadex
,记录句柄后启动暂停然后恢复,等待处理即可,CloseHandle
必须调用.
CreateThread()
用于在代码中使用任何CRT函数时发生内存泄漏._beginthreadex()
具有相同的参数,CreateThread()
并且它比它更通用_beginthread()
.所以我建议你使用_beginthreadex()
.
关于你更新的问题:" WaitForSingleObject()
如果我使用的话,我也会在几个地方看到我不能打电话_beginthread()
,但如果我_endthread()
在线程中打电话不应该那么有用吗?"
通常,您可以将线程句柄传递给WaitForSingleObject()
(或等待对象句柄的其他API),直到线程完成为止.但是,调用_beginthread()
时创建的线程句柄在_endthread()
调用时会关闭(可以显式完成,也可以在线程过程返回时由运行时隐式完成).
问题在文档中提到WaitForSingleObject()
:
如果在等待仍处于暂挂状态时关闭此句柄,则函数的行为未定义.
看功能签名,CreateThread
几乎完全相同_beginthreadex
.
_beginthread
,_beginthreadx
vsCreateThread
HANDLE WINAPI CreateThread( __in_opt LPSECURITY_ATTRIBUTES lpThreadAttributes, __in SIZE_T dwStackSize, __in LPTHREAD_START_ROUTINE lpStartAddress, __in_opt LPVOID lpParameter, __in DWORD dwCreationFlags, __out_opt LPDWORD lpThreadId ); uintptr_t _beginthread( void( *start_address )( void * ), unsigned stack_size, void *arglist ); uintptr_t _beginthreadex( void *security, unsigned stack_size, unsigned ( *start_address )( void * ), void *arglist, unsigned initflag, unsigned *thrdaddr );
这里的评论说_beginthread
可以使用__cdecl
或者__clrcall
调用约定作为起始点,并且_beginthreadex
可以使用__stdcall
或者__clrcall
用于起始点.
我认为人们对内存泄漏的评论CreateThread
已有十多年的历史,应该被忽略.
有趣的是,这两个_beginthread*
函数实际调用CreateThread
的引擎盖下,在C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\crt\src
我的机器上.
// From ~line 180 of beginthreadex.c /* * Create the new thread using the parameters supplied by the caller. */ if ( (thdl = (uintptr_t) CreateThread( (LPSECURITY_ATTRIBUTES)security, stacksize, _threadstartex, (LPVOID)ptd, createflag, (LPDWORD)thrdaddr)) == (uintptr_t)0 ) { err = GetLastError(); goto error_return; }