多线程程序中的fork

Posted

技术标签:

【中文标题】多线程程序中的fork【英文标题】:fork in multi-threaded program 【发布时间】:2010-11-17 03:45:39 【问题描述】:

我听说在程序中混合分叉和线程可能会产生很大的问题,通常会导致神秘的行为,尤其是在处理共享资源时,例如锁、管道、文件描述符。但我从来没有完全理解危险到底是什么,什么时候会发生。如果有这方面专业知识的人能更详细地解释一下什么是陷阱以及在这样的环境中编程时需要注意什么,那就太好了。

例如,如果我想编写一个从各种不同资源收集数据的服务器,我想到的一个解决方案是让服务器产生一组线程,每个线程都会调用另一个程序来完成实际工作,打开管道从孩子那里取回数据。这些线程中的每一个都响应自己的工作,它们之间没有数据交换,当收集数据时,主线程有一个队列,这些工作线程只会将结果放入队列中。这个解决方案会出现什么问题?

请不要仅仅通过“回答”我的示例场景来缩小您的答案。任何与示例无关但有助于提供简洁设计的建议、替代解决方案或经验都会很棒!谢谢!

【问题讨论】:

这里是good read,其中包含有关该主题的更多详细信息- 【参考方案1】:

当你确实有一些线程在运行时,fork 的问题是,fork 只复制调用它的一个线程的 CPU 状态。就好像所有其他线程都立即死亡,无论它们在哪里。

这样的结果是锁没有被释放,共享数据(例如 malloc 堆)可能被破坏。

pthread 确实提供了一个pthread_atfork 函数 - 理论上,您可以在分叉之前获取程序中的每个锁,然后释放它们,也许 让它活着 - 但这是有风险的,因为你总是会错过一个。当然,其他线程的堆栈也不会被释放。

【讨论】:

您能否详细说明“未释放锁”是什么意思?从孩子的角度来看对吗?所以孩子永远无法获得锁? 正确。当所有锁仍处于锁定状态时,fork 会克隆所有锁。【参考方案2】:

这真的很简单。多线程和多进程的问题总是来自共享数据。如果没有共享数据,则不可能出现问题。

在您的示例中,共享数据是主线程拥有的队列 - 任何潜在的争用或竞争条件都会出现在这里。 “解决”这些问题的典型方法涉及锁定方案——工作线程将在插入任何数据之前锁定队列,而主线程将在删除队列之前锁定队列。

【讨论】:

我不知道——但通常你的标准库是用线程安全的方式编写的(有时你必须选择它们的线程安全版本)。这取决于您对共享数据的定义以及影响是什么。 通常,我们无法避免共享数据。像管道、文件描述符等。它们总是在 fork 之间共享。现在在 linux 下,可以设置 O_CLOEXEC 标志,以便在分叉时关闭 fd,(我猜这意味着关闭子地址空间上的 fd),但我不知道如果我们在其中添加线程是否会有所帮助?例如如果我用一根螺纹和叉子打开管道怎么办?如果另一个线程也分叉怎么办?哪个孩子能看到管道? @bdonlan - 不,malloc() 只使用进程内存。 @jimx - 进程中的线程共享 FD,虽然我不知道 O_CLOEXEC - 无论哪个线程调用 fork(),效果都是一样的。 O_CLOEXEC 标志不会导致文件描述符在 fork 上关闭,顾名思义它在 exec 上关闭。

以上是关于多线程程序中的fork的主要内容,如果未能解决你的问题,请参考以下文章

谨慎使用多线程中的fork

[转]极不和谐的 fork 多线程程序

多线程下的fork问题(模拟与解决)

多线程下的fork问题(模拟与解决)

多线程下的fork问题(模拟与解决)

谨慎使用多线程中的fork