【问题标题】:How to properly use _beginthread and endthread如何正确使用_beginthread和endthread
【发布时间】:2015-09-23 11:57:56
【问题描述】:

我习惯于使用良好的旧 WinAPI 调用 CreateThread(),并使用等待函数检查线程的状态,例如 WaitForSingleObject(),一旦使用 WAIT_OBJECT_0 发出线程线程信号,我使用 CloseHandle(). 将其关闭

最近我决定搬到beginthread 并以某种方式避免未初始化crt 和可能发生的意外内存泄漏的风险。

这样做让我很困惑。

  1. endthread() 的确切用途是什么?为什么当我在 main 函数中调用 CloseHandle() 时,线程执行后,CloseHandle() 会因句柄无效而崩溃?
  2. 我应该关闭beginthread返回的句柄吗?
  3. endthread,据我所知,一旦我的函数超出范围,线程会自动调用它,所以我应该在超出范围之前调用它吗?
  4. 根据msdn,endthread已经调用了CloseHandle() 1.线程从哪里获得对其句柄的引用/实例。 2. 如果我坚持使用endthread(),它应该是线程中的最后一个命令吗?

谢谢

编辑: 描述泄漏的 MSDN 论文,here

【问题讨论】:

  • 您可以免费使用CreateThread。未初始化 CRT 的危险很久以前就被消除了。至于你的代码有什么问题,只有你自己能看到。您忘记附加 MCVE。
  • @DavidHeffernan 所以你认为 80 字节的泄漏是可以接受的! ;)
  • @deviantfan Hans Passant 在这里多次声明。正如他常说的,如果这个问题很久没有解决,如何从 MSVC 访问系统线程池?例如:stackoverflow.com/questions/2100589/beginthread-vs-createthread
  • 无论你选择哪个调用,如果你需要对返回的句柄进行操作,就永远不要使用_beginthread。这在_beginthread, _beginthreadex 的文档中得到了很好的解释。
  • 每个线程数据可能泄漏的一种情况是与在 Windows XP(或更早版本)下运行的 CRT 的静态版本链接的可执行文件。与 CRT 的 DLL 版本链接的任何内容、与 CRT 的静态版本链接的 DLL 或在 Server 2003 或 Vista 或更高版本下运行的任何内容都没有泄漏。

标签: c++ multithreading winapi


【解决方案1】:

正如 David Hefferman 在 cmets 中所述,您可以简单地将代码改回使用 CreateThread。当您第一次使用使用每线程数据的函数时,Visual C++ 运行时 (CRT) 将自动初始化 CRT 的每线程数据。

CRT 还会在线程结束时自动释放每个线程的数据,因此使用 CreateThread 不会导致内存泄漏。有一个例外,如果以下所有条件都为真,则不会自动释放每个线程的数据:

  • 您正在构建一个可执行文件,而不是一个 DLL
  • 您链接的是静态版本的 CRT 库 (LIBCMT.LIB),而不是 DLL 版本 (MSVCRT.LIB)
  • 您构建的可执行文件在 Windows XP(或更早版本的 Windows)下运行。

请注意,即使在您的情况下所有这些都是正确的,内存泄漏也不会很严重,除非您正在创建一个长期存在的应用程序,该应用程序在其生命周期内创建和销毁数十万个线程。

如果您仍想使用 CRT 线程创建功能 (_beginthread/_beginthreadex),您应该遵循以下准则:

  • 切勿使用_beginthread 返回的句柄。使用_beginthread,线程句柄在线程退出时自动关闭,这可能在_beginthread 甚至返回之前发生。您不能安全地将它与 WaitForSingleObject 一起使用,因为在您调用此函数之前线程可能已经退出。如果您想将线程句柄用于任何事情,请改用_beginthreadex
  • 您不应该关闭_beginthread 返回的句柄。 CRT 会自动执行此操作,并且如前一点所述,可能会在您有机会之前执行此操作。
  • 当您不再需要 _beginthreadex 返回的句柄时,应始终关闭它。 CRT 不会自动为您执行此操作,因此这是您自己的责任。
  • 不要调用_endthread_endthreadex,除非你想快速异常终止线程。虽然 CRT 将释放它自己的每个线程数据,但不会调用任何线程对象的 C++ 析构函数。它的行为类似于 _exit 在不调用析构函数的情况下结束进程的方式。
  • 结束线程的正常方法应该是从作为参数传递给_beginthread_beginthreadex 的函数返回。这将导致 C++ 析构函数作为函数返回的正常部分被调用。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-01-20
    • 2011-01-07
    • 2019-01-31
    • 2023-04-03
    • 2018-12-04
    • 2018-03-22
    • 2020-07-02
    • 2022-01-18
    相关资源
    最近更新 更多