c++ - Windows 线程:

c++ - Windows 线程:

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 回答

相关数据

飞禽和走兽(飞禽和走兽分类)
Bet体育365第二次提款要多久

飞禽和走兽(飞禽和走兽分类)

⌚ 08-20 👁️‍🗨️ 3065
乐视致新
Bet体育365第二次提款要多久

乐视致新

⌚ 07-16 👁️‍🗨️ 9309
专业咖啡冲煮:咖啡萃取时间、闷蒸时间、最佳冲煮数据
Bet体育365第二次提款要多久

专业咖啡冲煮:咖啡萃取时间、闷蒸时间、最佳冲煮数据

⌚ 07-01 👁️‍🗨️ 7173