17 回答 17
This answer is useful
99
CreateThread()是用于在内核级别创建另一个控制线程的原始 Win32 API 调用。
_beginthread()&是在幕后_beginthreadex()调用的 C 运行时库调用。CreateThread()一旦CreateThread()返回,_beginthread/ex()负责额外的簿记以使 C 运行时库在新线程中可用且一致。
在 C++ 中,您几乎肯定应该使用_beginthreadex(),除非您根本不会链接到 C 运行时库(又名 MSVCRT*.dll/.lib)。
于 2008-12-01T17:29:57.843 回答
This answer is useful
39
_beginthread()和之间有几个区别_beginthreadex()。 _beginthreadex()被做得更像CreateThread()(在参数和行为方式上)。
正如Drew Hall所提到的,如果您使用的是 C/C++ 运行时,则必须使用_beginthread()/_beginthreadex()而不是,CreateThread()以便运行时有机会执行它自己的线程初始化(设置线程本地存储等)。
实际上,这意味着CreateThread()您的代码几乎不应该直接使用它。
_beginthread()/的 MSDN 文档_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。
于 2008-12-01T18:35:57.290 回答
This answer is useful
25
一般来说,正确的做法是调用_beginthread()/_endthread()(或ex()变体)。但是,如果您将 CRT 用作 .dll,则 CRT 状态将被正确初始化和销毁,因为 CRTDllMain将分别在调用DLL_THREAD_ATTACH和/或返回时被调用。DLL_THREAD_DETACHCreateThread()ExitThread()
CRT的DllMain代码可以在 VC\crt\src\crtlib.c 下的 VS 安装目录中找到。
于 2012-10-19T00:01:15.393 回答
This answer is useful
17
这是_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 将正常工作。
于 2008-12-01T18:33:16.017 回答
This answer is useful
14
您应该使用_beginthread或_beginthreadex允许 C 运行时库自己初始化线程。只有 C/C++ 程序员需要知道这一点,因为他们现在应该知道使用自己的开发环境的规则。
如果您使用_beginthread,则不需要调用CloseHandle,因为 RTL 会为您做。这就是为什么如果您使用过_beginthread. 如果_beginthread线程函数立即(快速)退出,也会导致混乱,因为启动线程可能会持有对它刚刚启动的线程的无效线程句柄。
_beginthreadex句柄可用于等待,但也需要显式调用CloseHandle. 这是使它们可以安全使用等待的部分原因。使其完全万无一失的另一个问题是始终启动暂停的线程。检查成功,记录句柄等。恢复线程。这是为了防止线程在启动线程可以记录其句柄之前终止。
最好的做法是使用_beginthreadex,记录句柄后开始暂停然后恢复,等待句柄是可以的,CloseHandle必须调用。
于 2011-04-12T11:44:32.263 回答
This answer is useful
9
CreateThread()当您在代码中使用任何 CRT 函数时,曾经有内存泄漏。_beginthreadex()具有与 相同CreateThread()的参数,并且比_beginthread(). 所以我推荐你使用_beginthreadex().
于 2008-12-01T18:21:10.440 回答
This answer is useful
6
关于您更新的问题:“我还阅读了几个WaitForSingleObject()如果我使用过就无法调用的地方_beginthread(),但是如果我_endthread()在线程中调用,那不应该工作吗?”
通常,您可以将线程句柄WaitForSingleObject()(或等待对象句柄的其他 API)传递给阻塞,直到线程完成。但是由创建的线程句柄在被调用_beginthread()时关闭_endthread()(这可以显式完成,也可以在线程过程返回时由运行时隐式完成)。
该问题在以下文档中被指出WaitForSingleObject():
如果在等待仍处于挂起状态时关闭此句柄,则函数的行为未定义。
于 2008-12-01T19:35:30.600 回答
This answer is useful
5
查看函数签名,CreateThread几乎与_beginthreadex.
_beginthread,_beginthreadx与CreateThread
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;
}
于 2011-06-02T20:44:48.103 回答
This answer is useful
3
beginthreadex给你一个线程HANDLE供WaitForSingleObject朋友们使用。beginthread没有。完成后不要忘记CloseHandle()。真正的答案是使用boost::thread或很快使用 C++09 的线程类。
于 2008-12-01T18:32:58.450 回答
This answer is useful
2
与 相比_beginthread,_beginthreadex您可以:
指定安全属性。
启动处于挂起状态的线程。
您可以获得可以与OpenThread.
如果调用成功,则保证返回的线程句柄有效。在那里你需要用 关闭手柄CloseHandle。
返回的线程句柄可以与同步 API 一起使用。
与_beginthreadex非常相似CreateThread,但前者是 CRT 实现,后者是 Windows API 调用。CreateThread的文档包含以下建议:
调用 C 运行时库 (CRT) 的可执行文件中的线程应该使用_beginthreadexand_endthreadex函数进行线程管理,而不是CreateThreadand ExitThread;这需要使用 CRT 的多线程版本。如果使用创建的线程CreateThread调用 CRT,CRT 可能会在内存不足的情况下终止进程。
于 2011-09-23T10:17:39.093 回答
This answer is useful
2
CreateThread()是语言中立的 Windows API 调用。它只是创建 OS 对象 - 线程并将 HANDLE 返回给该线程。所有 Windows 应用程序都使用此调用来创建线程。所有语言都避免直接调用 API,原因很明显: 1. 你不希望你的代码是特定于操作系统的 2. 在调用类似 API 之前你需要做一些内务:转换参数和结果,分配临时存储等。
_beginthreadex()CreateThread()是围绕特定于 C的 C 包装器。它通过分配线程特定的存储,使原始的单线程 C f-ns 在多线程环境中工作。
如果您不使用 CRT,则无法避免直接调用CreateThread(). 如果你使用 CRT,你必须使用_beginthreadex()VC2005 之前的一些 CRT 字符串 f-ns 可能无法正常工作。
于 2012-05-15T17:50:34.903 回答
This answer is useful
2
CreateThread()曾经是一个禁忌,因为 CRT 会被错误地初始化/清理。但这现在已成为历史:现在(使用 VS2010 和可能的几个版本)可以CreateThread()在不破坏 CRT 的情况下调用。
这是官方的 MS 确认。它声明了一个例外:
实际上,唯一不应该在创建的线程中使用的函数CreateThread()是signal()函数。
但是,从一致性的角度来看,我个人更喜欢继续使用_beginthreadex().
于 2012-10-12T12:14:24.183 回答
This answer is useful
2
CreateThread()是直接的系统调用。它的实现Kernel32.dll很可能由于其他原因您的应用程序已经被链接。它在现代 Windows 系统中始终可用。
_beginthread()并且_beginthreadex()是 Microsoft C 运行时 ( msvcrt.dll) 中的包装函数。文档中说明了这两个调用之间的差异。因此,当 Microsoft C 运行时可用时,或者如果您的应用程序与其静态链接时,它就可用。您也可能会链接到该库,除非您使用纯 Windows API 进行编码(就像我个人经常做的那样)。
您的问题是连贯的,实际上是一个反复出现的问题。与许多 API 一样,我们必须处理 Windows API 中的重复和模棱两可的功能。最糟糕的是,文档没有澄清这个问题。我认为_beginthread()创建函数系列是为了更好地与其他标准 C 功能集成,例如对errno. _beginthread()从而更好地与 C 运行时集成。
尽管如此,除非您有充分的理由使用_beginthread()or _beginthreadex(),否则您应该使用CreateThread(),主要是因为您可能会在最终的可执行文件中减少一个库依赖项(对于 MS CRT,这确实很重要)。您也没有围绕调用的包装代码,尽管这种影响可以忽略不计。换句话说,我认为坚持的主要原因CreateThread()是没有充分的理由_beginthreadex()开始使用。功能完全相同或几乎相同。
使用的一个很好的理由_beginthread() 是(因为它似乎是错误的)如果_endthread()被调用,C++ 对象将被正确地展开/销毁。
于 2012-10-18T12:51:02.180 回答
This answer is useful
1
如果您阅读 Jeffrey Richter 的《调试 Windows 应用程序》一书,他解释说几乎在所有情况下您都必须调用_beginthreadex而不是调用CreateThread. _beginthread只是一个简化的包装器_beginthreadex。
_beginthreadexCreateThread初始化API 不会执行的某些 CRT (C RunTime) 内部。
如果您使用CreateThreadAPI 而不是使用_begingthreadex对 CRT 函数的调用,结果可能会导致意外的问题。
查看 Richter 的这本旧 Microsoft Journal。
于 2014-02-15T20:41:23.367 回答
This answer is useful
0
其他答案未能讨论调用包装 Win32 API 函数的 C 运行时函数的含义。这在考虑 DLL 加载程序锁定行为时很重要。
正如其他答案所讨论的那样,无论是否_beginthread{ex}有任何特殊的 C 运行时线程/光纤内存管理,它都是在进程可能尚未加载的 DLL 中实现的(假设动态链接到 C 运行时)。
_beginthread*从调用是不安全的DllMain。我已经通过编写使用 Windows“AppInit_DLLs”功能加载的 DLL 对此进行了测试。调用_beginthreadex (...)而不是CreateThread (...)导致 Windows 的许多重要部分在启动期间停止运行,因为DllMain入口点死锁等待加载程序锁被释放以执行某些初始化任务。
顺便说一句,这也是为什么kernel32.dll有很多重叠的字符串函数,C 运行时也有——使用这些函数DllMain来避免相同的情况。
于 2016-09-16T07:15:47.447 回答
This answer is useful
-1
你应该试试这段代码
#include
#include
#include
#include
UINT __stdcall Staff(PVOID lp){
printf("The Number is %d\n", GetCurrentThreadId());
return 0;
}
INT main(INT argc, PCHAR argv[])
{
const INT Staff_Number = 5;
HANDLE hd[Staff_Number];
for(INT i=0; i < Staff_Number; i++){
hd[i] = (HANDLE)_beginthreadex(NULL, 0, Staff, NULL, 0, NULL);
}
WaitForMultipleObjects(Staff_Number, Staff, TRUE, NULL);
for(INT i=0; i < Staff_Number; i++)
{
CloseHandle(hd[i]);
}
system("pause");
return 0;
}
如果您使用 _beginthread 而不是 _beginthreadex 它会给 _beginthread 错误太多参数,这是因为 _beginthread 无法创建具有安全属性的线程,而且我认为 _beginthread 是不必要的,您绝对可以使用 *(_beginthreadex) 和 CreateThread
于 2020-08-08T09:50:47.230 回答
This answer is useful
-2
两者之间已经没有区别了。
所有关于内存泄漏等的评论都是基于非常旧的 < VS2005 版本。几年前我做过一些压力测试,可以揭穿这个神话。甚至 Microsoft 在他们的示例中也混合了这些样式,几乎从不使用 _beginthread。
于 2015-05-04T04:42:31.500 回答