当前位置: 首页 > 知识库问答 >
问题:

为什么在线程销毁之前必须调用join()或detach()?

芮雪风
2023-03-14

我不明白为什么当一个std::线程被破坏时,它必须处于连接()或分离()状态。

连接等待线程完成,而分离不会。似乎有一些我不理解的中间状态。因为我的理解是连接和分离是互补的:如果我不调用连接(),那么分离()是默认的。

逻辑上的detach()应该是线程的默认行为,因为这是线程的定义,它们是并行执行的,与其他线程无关。

那么,当线程对象被破坏时,为什么要调用terminate()呢?为什么标准不能简单地将线程视为分离的线程?

我不理解在线程被破坏之前,当连接()或分离()没有被调用时终止程序背后的原理。这样做的目的是什么?

更新:

我最近发现了这个。安东尼·威廉姆斯在他的书《行动中的并发》中说,“C 17的一个建议是建立一个类似于std::线程的joining_thread类,只是它会像scoped_thread一样自动加入析构函数。这在委员会中没有得到共识,所以它没有被标准所接受(尽管它仍在C20的轨道上,d::jthray)..."

共有3个答案

柯波
2023-03-14

您可能希望线程在完成后完全清理自己,而不留下任何痕迹。这意味着你可以开始一个线程,然后忘记它。

但是,您可能还希望能够在线程运行时管理它,并获得它在运行时提供的任何返回值。在这种情况下,如果一个线程在完成后自行清理,您试图管理它可能会导致崩溃,因为您将访问一个可能无效的句柄。为了在线程完成时检查返回值,返回值必须存储在某个地方,这意味着线程不能完全清理,因为存储返回值的地方必须保留。

在大多数框架中,默认情况下,您会得到第二个选项。您可以管理线程(通过中断它、向它发送信号、加入它或其他方式),但它不能自行清理。如果您更喜欢第一个选项,有一个函数可以获得该行为(分离),但这意味着您可能无法访问线程,因为它可能会继续存在,也可能不会继续存在。

陶健
2023-03-14

您会感到困惑,因为您将std::thread对象与其引用的执行线程混为一谈。std::thread对象是一个C对象(内存中的一组字节),用作执行线程的引用。调用std::thread::detach时,发生的情况是std::thread对象与执行线程“分离”——它不再引用(任何)执行线程,执行线程继续独立运行。但是std::thread对象仍然存在,直到它被销毁。

当一个执行线程完成时,它将其退出信息存储到引用它的std::thread对象中,如果有一个(如果它被分离,那么就没有一个,因此退出信息被丢弃)它对std::thread对象没有其他影响,尤其是std::thread对象不会被销毁,并且会一直存在,直到其他人销毁它。

法子昂
2023-03-14

从技术上来说,答案是“因为规范这么说”,但这是一个迟钝的答案。我们无法理解设计师的想法,但这里有一些问题可能是其中的原因:

对于POSIX线程,子线程必须在退出后加入,否则它们将继续占用系统资源(如内核中的进程表项)。这是通过pthread_join()完成的。如果进程持有子线程的HANDLE,则Windows有一个有点类似的问题;尽管Windows不需要完全连接,但进程仍然必须调用CloseHandle()来释放线程上的重新计数。

由于std::thread是一个跨平台的抽象,它受到POSIX要求的约束,POSIX要求连接。

理论上,d::线程析构函数可以调用pthread_join()而不是抛出异常,但这(主观上)可能会增加死锁的风险。而正确编写的html" target="_blank">程序会知道何时在安全时间插入连接。

另见:

  • https://en.wikipedia.org/wiki/Zombie_process
  • https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessa
  • https://docs.microsoft.com/en-us/windows/win32/procthread/terminating-a-process
 类似资料:
  • 问题内容: 每个人都告诉我“使用super.viewDidLoad()因为它就是这样”或“我一直那样做,所以要保留它”,“如果不叫super就是错误的”等。 我只发现了一些有关Objective-C用例的主题,这些主题并没有那么启发性,但是我正在Swift 3中进行开发,所以有什么专家可以给我一个很好的详细解释吗? 这是一种良好实践的案例还是有任何隐藏的影响? 问题答案: 通常,最好为您覆盖的所有

  • 问题内容: 我创建了一个用于显示工具提示的指令: 对应功能: 应用于此: 这是我观点的一部分,由拥有者的控制器处理 为什么必须调用才能将更改应用到,该更改是早先声明和初始化的? 问题答案: 因为附加到事件的回调超出了angular的范围;angular不知道该函数何时运行/结束,因此摘要循环永远不会运行。 调用或告诉angular更新绑定并触发任何手表。

  • 问题内容: 即使等待1的linux手册页很好地说明了您需要让子进程不使其变成僵尸,但它根本无法说明原因。 我围绕一个Ever 循环计划了我的程序(这是我的第一个多线程程序,所以请原谅我的天真),该循环启动子进程,该子进程被ed淘汰,并确保自行终止。 我无法使用,因为这使并行计算变得不可能,因此我可能不得不添加一个存储子pid的进程表,并且不得不使用-不是立即执行,而是经过一段时间- 这是一个问题,

  • 我得到以下错误: 一个月后,我正在检查我的程序,之前相同的代码没有给出错误,我想我没有改变任何可能导致这一点的东西。我试图解决这个问题;所以在第1行的错误中提到:

  • 问题内容: 我收到此错误。如下面的代码所示,该;行位于代码行之前;。在我的每一项活动中,此onCreate()代码都是相同的格式,到目前为止,我从没有遇到过麻烦。自从我将ADT更新为22以来,到处都出现了许多随机错误。我已经克服了许多错误,这是我的最新错误。 我该如何解决此错误? 问题答案: 我也遇到了这个问题,但是当我在调用super.onCreate()之前调用窗口请求时,问题就解决了,请尝试

  • 我是一个蟒蛇初学者。试着做一个新的按钮来关闭窗口。我得到了错误消息: 异常在Tkinter回调Traceback(最近调用最后):文件/系统/库/框架/Python.framework/Versions/2.7/lib/python2.7/lib-tk/Tkinter.py,第1536行,在调用返回self.func(*args)文件tk_cp_successful.py,第138行,在按钮推送s