windows多线程 - Windows线程:_beginthread vs_beginthreadex与CreateThread C++




c语言多线程 (11)

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

我试图确定_beginthread_beginthreadexCreateThread的优点/缺点。 所有这些函数都返回一个线程句柄给一个新创建的线程,我已经知道CreateThread在发生错误时提供了一些额外的信息(可以通过调用GetLastError来检查它)...但是当我使用这些功能?

我正在使用Windows应用程序,所以跨平台兼容性已经不存在了。

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

干杯!

更新:好的,谢谢所有的信息,我也读过几个地方,如果我使用_beginthread() ,我不能调用WaitForSingleObject() _beginthread() ,但如果我在线程中调用_endthread()不应该工作? 那里有什么交易?



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

_beginthread()_beginthreadex()是在后台调用CreateThread() C运行时库调用。 一旦CreateThread()返回, _beginthread/ex()负责额外的簿记工作,以使C运行时库在新线程中可用且一致。

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


CreateThread()是直接的系统调用。 它在Kernel32.dll上实现,最有可能的是,由于其他原因,您的应用程序已经被链接。 它始终可用于现代Windows系统。

_beginthread()_beginthreadex()是Microsoft C运行时( msvcrt.dll )中的包装函数。 这两个电话之间的差异在文档中有说明。 因此,当Microsoft C运行时可用时,或者您的应用程序静态链接到它时,它就可用。 除非你使用纯Windows API编写代码(正如我个人经常这样做的),否则你也可能会链接到该库。

你的问题是一个连贯的,实际上反复出现的问题。 尽可能多的API在我们必须处理的Windows API中存在重复和模糊的功能。 最糟糕的是,文件没有说明问题。 我猜想_beginthread()系列函数是为了更好地与其他标准C函数集成而创建的,例如errno的操作。 _beginthread()因此更好地与C运行时集成。

尽管如此,除非你有充分的理由使用_beginthread()_beginthreadex() ,你应该使用CreateThread() ,主要是因为你可能会在最终的可执行文件中获得一个较少的库依赖项(对于MS CRT而言,这有点重要)。 您的呼叫也没有包装代码,尽管这种效果可以忽略不计。 换句话说,我相信坚持使用CreateThread()主要原因是没有理由使用_beginthreadex()来开始。 功能正是或几乎相同。

使用_beginthread()一个很好的理由 (如同它似乎是错误的),如果调用了_endthread() ,那么C ++对象会被正确解开/销毁。


CreateThread()曾经是否定的,因为CRT将不正确地初始化/清理。 但现在这已经成为历史:现在可以(使用VS2010,可能还有几个版本CreateThread()不破坏CRT的情况下调用CreateThread()

这是官方的MS确认 。 它说明了一个例外:

实际上, CreateThread()创建的线程中不应该使用的唯一函数是signal()函数。

然而,从一致性的角度来看,我个人更喜欢继续使用_beginthreadex()


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


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

CRT的DllMain代码可在VC \ crt \ src \ crtlib.c中的VS的安装目录中找到。


两者之间没有区别。

所有关于内存泄漏的评论都是基于很老的<VS2005版本。 多年前我已经做过一些压力测试,可以揭穿这个神话。 即使微软在他们的例子中混合了样式,几乎从不使用_beginthread。


关于你更新的问题:“我还读过几个地方,如果我使用_beginthread() ,但不能调用WaitForSingleObject() _beginthread() ,但是如果我在线程中调用_endthread()应该不行?”

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

该问题在WaitForSingleObject()的文档中被调用:

如果这个句柄在等待仍然未决时关闭,则该函数的行为是未定义的。


如果您阅读Jeffrey Richter的“调试Windows应用程序”一书,他解释说,几乎在所有情况下,您都必须调用_beginthreadex而不是调用CreateThread_beginthread只是_beginthread的简化包装。

_beginthreadex初始化CreateThread API不会执行的某些CRT(C运行时间)内部消息。

如果使用CreateThread API而不是使用_begingthreadex调用CRT函数,结果可能会导致意想不到的原因问题。

查看这份来自Richter的旧微软杂志。


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

如果你使用_beginthread你不需要调用CloseHandle因为RTL会为你做。 这就是为什么如果你已经使用了_beginthread你不能等待句柄。 如果线程函数立即(快速)退出,则_beginthread会导致混淆,因为启动的线程会在其刚刚启动的线程中留下无效的线程句柄。

_beginthreadex句柄可用于等待,但也需要明确调用CloseHandle 。 这是什么使他们安全等待使用的一部分。 还有其他问题使它完全万无一失就是始终启动暂停的线程。 检查成功,记录句柄等简历线程。 这是在启动线程可以记录其句柄之前阻止线程终止所必需的。

最佳做法是使用_beginthreadex ,启动暂停然后恢复录制句柄后,等待句柄确定,必须调用CloseHandle


这是_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调用将正常工作。





winapi