Windows 线程:_beginthread 与 _beginthreadex 与 CreateThread C++

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/331536/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 14:41:26  来源:igfitidea点击:

Windows threading: _beginthread vs _beginthreadex vs CreateThread C++

c++cmultithreadingwinapi

提问by Kiril

What's a better way to start a thread, _beginthread, _beginthreadxor CreateThread?

启动线程的更好方法是什么,_beginthread_beginthreadxCreateThread

I'm trying to determine what are the advantages/disadvantages of _beginthread, _beginthreadexand CreateThread. All of these functions return a thread handle to a newly created thread, I already know that CreateThread provides a little extra information when an error occurs (it can be checked by calling GetLastError)... but what are some things I should consider when I'm using these functions?

我试图确定_beginthread,_beginthreadex和的优点/缺点是什么CreateThread。所有这些函数都会向新创建的线程返回一个线程句柄,我已经知道 CreateThread 在发生错误时提供了一些额外的信息(可以通过调用来检查GetLastError)……但是我应该考虑哪些事情? m 使用这些功能?

I'm working with a windows application, so cross-platform compatibility is already out of the question.

我正在使用 Windows 应用程序,因此跨平台兼容性已经不可能了。

I have gone through the msdn documentation and I just can't understand, for example, why anybody would decide to use _beginthread instead of CreateThread or vice versa.

我已经阅读了 msdn 文档,但我无法理解,例如,为什么有人会决定使用 _beginthread 而不是 CreateThread,反之亦然。

Cheers!

干杯!

Update: OK, thanks for all the info, I've also read in a couple of places that I can't call WaitForSingleObject()if I used _beginthread(), but if I call _endthread()in the thread shouldn't that work? What's the deal there?

更新:好的,感谢您提供的所有信息,我还在几个地方读到了WaitForSingleObject()如果我使用了就无法调用的地方_beginthread(),但是如果我调用_endthread()线程不应该这样吗?那里有什么交易?

采纳答案by Drew Hall

CreateThread()is a raw Win32 API call for creating another thread of control at the kernel level.

CreateThread()是一个原始的 Win32 API 调用,用于在内核级别创建另一个控制线程。

_beginthread()& _beginthreadex()are C runtime library calls that call CreateThread()behind the scenes. Once CreateThread()has returned, _beginthread/ex()takes care of additional bookkeeping to make the C runtime library usable & consistent in the new thread.

_beginthread()&_beginthreadex()CreateThread()在幕后调用的 C 运行时库调用。一旦CreateThread()返回,_beginthread/ex()就负责额外的簿记,以使 C 运行时库在新线程中可用并保持一致。

In C++ you should almost certainly use _beginthreadex()unless you won't be linking to the C runtime library at all (aka MSVCRT*.dll/.lib).

在 C++ 中,您几乎肯定应该使用,_beginthreadex()除非您根本不会链接到 C 运行时库(又名 MSVCRT*.dll/.lib)。

回答by Michael Burr

There are several differences between _beginthread()and _beginthreadex(). _beginthreadex()was made to act more like CreateThread()(in both parameters and how it behaves).

_beginthread()和之间有几个区别_beginthreadex()_beginthreadex()表现得更像CreateThread()(在参数和行为方式上)。

As Drew Hallmentions, if you're using the C/C++ runtime, you must use _beginthread()/_beginthreadex()instead of CreateThread()so that the runtime has a chance to perform it's own thread initialization (setting up thread local storage, etc.).

正如Drew Hall提到的,如果您使用的是 C/C++ 运行时,则必须使用_beginthread()/_beginthreadex()而不是,CreateThread()以便运行时有机会执行它自己的线程初始化(设置线程本地存储等)。

In practice, this means that CreateThread()should pretty much never be used directly by your code.

在实践中,这意味着CreateThread()您的代码几乎不应该直接使用它。

The MSDN documents for _beginthread()/_beginthreadex()have quite a bit of detail on the differences - one of the more important is that since the thread handle for a thread created by _beginthread()gets closed automatically by the CRT when the thread exits, "if the thread generated by _beginthread exits quickly, the handle returned to the caller of _beginthread might be invalid or, worse, point to another thread".

_beginthread()/的 MSDN 文档对_beginthreadex()差异有相当多的细节 - 其中一个更重要的是,由于_beginthread()由 _beginthread创建的线程的线程句柄在线程退出时由 CRT 自动关闭,“如果由 _beginthread 生成的线程退出很快,返回给 _beginthread 调用者的句柄可能无效,或者更糟糕的是,指向另一个线程”。

Here is what the comments for _beginthreadex()in the CRT source have to say:

以下是_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).

UpdateJan 2013:

2013 年 1 月更新

The CRT for VS 2012 has an additional bit of initialization performed in _beginthreadex(): if the process is a "packaged app" (if something useful is returned from GetCurrentPackageId()) the runtime will initialize the MTA on the newly created thread.

VS 2012 的 CRT 在 中执行了额外的初始化_beginthreadex():如果进程是“打包的应用程序”(如果从 中返回有用的东西GetCurrentPackageId()),则运行时将在新创建的线程上初始化 MTA。

回答by MSN

In general, the correct thing to do is to call _beginthread()/_endthread()(or the ex()variants). However, if you use the CRT as a .dll, the CRT state will be properly initialized and destroyed as the CRT's DllMainwill be called with DLL_THREAD_ATTACHand DLL_THREAD_DETACHwhen calling CreateThread()and ExitThread()or returning, respectively.

一般来说,正确的做法是调用_beginthread()/_endthread()(或ex()变体)。但是,如果您使用CRT作为一个.dll,在CRT状态将被正确的初始化和销毁CRT的DllMain将被用DLL_THREAD_ATTACHDLL_THREAD_DETACH打电话时CreateThread()ExitThread()或分别返回。

The DllMaincode for the CRT can be found in the install directory for VS under VC\crt\src\crtlib.c.

DllMainCRT的代码可以在 VS 的安装目录 VC\crt\src\crtlib.c 下找到。

回答by Constantin

This is the code at the core of _beginthreadex(see crt\src\threadex.c):

这是_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;
    }

The rest of _beginthreadexinitializes per-thread data structure for CRT.

其余部分_beginthreadex为 CRT 初始化每线程数据结构。

The advantage of using _beginthread*is that your CRT calls from thread will work correctly.

使用的好处_beginthread*是您的 CRT 线程调用将正常工作。

回答by jarcher7

You should use _beginthreador _beginthreadexto allow the C runtime library to do it's own initialization of the thread. Only C/C++ programmers need to know this as they should now the rules of using their own development environment.

您应该使用_beginthread_beginthreadex允许 C 运行时库执行它自己的线程初始化。只有 C/C++ 程序员需要知道这一点,因为他们现在应该了解使用自己的开发环境的规则。

If you use _beginthreadyou do not need to call CloseHandleas the RTL will do for you. This is why you cannot wait on the handle if you have used _beginthread. Also _beginthreadleads to confusion if the thread function exits immediately (quickly) as the launching thread my be left holding an invalid thread handle to the thread it just launched.

如果你使用_beginthread你不需要调用,CloseHandle因为 RTL 会为你做。这就是为什么如果您使用了_beginthread. 也_beginthread导致混乱,如果线程函数立即退出(快速)作为启动线程我可以左手拿着无效的线程句柄,它刚刚推出的线程。

_beginthreadexhandles can be used for wait but also require an explicit call to CloseHandle. This is part of what makes them safe for using with wait. There other issue to make it completely foolproof is to always start the thread suspended. Check for success, record handle etc. The resume thread. This is required to prevent a thread from terminating before the launching thread can record its handle.

_beginthreadex句柄可用于等待,但也需要显式调用CloseHandle. 这是使它们可以安全地与等待一起使用的部分原因。使其完全万无一失的另一个问题是始终启动挂起的线程。检查是否成功,记录句柄等。恢复线程。这是防止线程在启动线程可以记录其句柄之前终止所必需的。

Best practice is to use _beginthreadex, start suspended then resume after recording handle, wait on handle is OK, CloseHandlemust be called.

最好的做法是使用_beginthreadex,开始挂起然后在记录句柄后恢复,等待句柄就可以了,CloseHandle必须调用。

回答by Jaywalker

CreateThread()used to have memory leakswhen you use any CRT functions in your code. _beginthreadex()has same parameters as CreateThread()and it's more versatile than _beginthread(). So I recommend you use _beginthreadex().

CreateThread()当您在代码中使用任何 CRT 函数时,过去常常会出现内存泄漏_beginthreadex()具有相同的参数,CreateThread()并且比_beginthread(). 所以我建议你使用_beginthreadex().

回答by Michael Burr

Regarding your updated question: "I've also read in a couple of places that I can't call WaitForSingleObject()if I used _beginthread(), but if I call _endthread()in the thread shouldn't that work?"

关于你更新的问题:“我也在几个地方读过,WaitForSingleObject()如果我使用了_beginthread(),我就无法调用,但是如果我调用_endthread()线程,那不应该吗?”

In general, you can pass a thread handle to WaitForSingleObject()(or other APIs that wait on object handles) to block until the thread has completed. But the thread handle created by _beginthread()is closed when _endthread()is called (which can be done explicitly or is done implicitly by the run time when the thread procedure returns).

通常,您可以将线程句柄传递给WaitForSingleObject()(或其他等待对象句柄的 API)以阻塞直到线程完成。但是创建的线程句柄_beginthread()_endthread()被调用时关闭(这可以显式完成,也可以在线程过程返回时由运行时隐式完成)。

The problem is called out in the documentation for WaitForSingleObject():

问题在以下文档中提到WaitForSingleObject()

If this handle is closed while the wait is still pending, the function's behavior is undefined.

如果此句柄在等待仍处于挂起状态时关闭,则函数的行为未定义。

回答by bobobobo

Looking at the function signatures, CreateThreadis almost identical to _beginthreadex.

查看函数签名,CreateThread几乎与_beginthreadex.

_beginthread, _beginthreadxvs CreateThread

_beginthread,_beginthreadxCreateThread

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

The remarks on heresay _beginthreadcan use either __cdeclor __clrcallcalling convention as start point, and _beginthreadexcan use either __stdcallor __clrcallfor start point.

这里的注释说_beginthread可以使用__cdecl__clrcall调用约定作为起点,也_beginthreadex可以使用__stdcall__clrcall为起点。

I think any comments people made on memory leaks in CreateThreadare over a decade old and should probably be ignored.

我认为人们对内存泄漏所做的任何评论CreateThread都有十多年的历史,应该被忽略。

Interestingly, both _beginthread*functions actually call CreateThreadunder the hood, in C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\crt\srcon my machine.

有趣的是,这两个_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;
}

回答by bobobobo

beginthreadexgives you a thread HANDLEfor use in WaitForSingleObjectand friends. beginthreaddoesn't. Don't forget to CloseHandle()when you are done. The real answer would be to use boost::threador soon C++09's thread class.

beginthreadex给你一个线程HANDLE供在WaitForSingleObject和朋友使用。beginthread没有。完成后不要忘记CloseHandle()。真正的答案是使用boost::thread或很快使用C++09 的线程类。

回答by Vishal

Compared to _beginthread, with _beginthreadexyou can:

与 相比_beginthread_beginthreadex您可以:

  1. Specify security attributes.
  2. Start a thread in suspended state.
  3. You can get the thread id which can be used with OpenThread.
  4. The thread handle returned is guaranteed to be valid if the call was successful. There for you need to close the handle with CloseHandle.
  5. The thread handle returned can be used with synchronization APIs.
  1. 指定安全属性。
  2. 启动处于挂起状态的线程。
  3. 您可以获得可以与OpenThread.
  4. 如果调用成功,则保证返回的线程句柄有效。因为你需要用 关闭手柄CloseHandle
  5. 返回的线程句柄可用于同步 API。

The _beginthreadexclosely resembles CreateThread, but the former is a CRT implementation and the latter a Windows API call. The documentation for CreateThreadcontains the following recommendation:

_beginthreadex酷似CreateThread,但前者是CRT实现,而后者Windows API调用。CreateThread的文档包含以下建议:

A thread in an executable that calls the C run-time library (CRT) should use the _beginthreadexand _endthreadexfunctions for thread management rather than CreateThreadand ExitThread; this requires the use of the multi-threaded version of the CRT. If a thread created using CreateThreadcalls the CRT, the CRT may terminate the process in low-memory conditions.

调用 C 运行时库 (CRT) 的可执行文件中的线程应该使用_beginthreadexand_endthreadex函数进行线程管理,而不是使用CreateThreadand ExitThread;这需要使用 CRT 的多线程版本。如果使用创建的线程CreateThread调用 CRT,则 CRT 可能会在内存不足的情况下终止进程。