从线程内分叉是否安全?

新手上路,请多包涵

让我解释一下:我已经在 Linux 上开发了一个应用程序,它分叉并执行一个外部二进制文件并等待它完成。结果由 fork + 进程独有的 shm 文件传达。整个代码封装在一个类中。

现在我正在考虑线程化进程以加快速度。有许多不同的类函数实例同时派生和执行二进制文件(使用不同的参数),并使用它们自己独特的 shm 文件来传达结果。

这个线程安全吗?如果我在一个线程中分叉,除了安全之外,还有什么需要注意的吗?非常感谢任何建议或帮助!

原文由 Ælex 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 553
2 个回答

问题是 fork() 只复制调用线程,并且子线程中持有的任何互斥锁将永远锁定在分叉的子线程中。 pthread 解决方案是 pthread_atfork() 处理程序。这个想法是您可以注册 3 个处理程序:一个 prefork、一个父处理程序和一个子处理程序。当 fork() 发生 prefork 在 fork 之前被调用并且预计会获得所有应用程序互斥体。父进程和子进程都必须分别释放父进程和子进程中的所有互斥锁。

但这不是故事的结局!库调用 pthread_atfork 为库特定的互斥锁注册处理程序,例如 Libc 这样做。这是一件好事:应用程序不可能知道第三方库持有的互斥锁,因此每个库必须调用 pthread_atfork 以确保在发生 fork()

问题是 pthread_atfork 处理程序调用无关库的顺序是未定义的(这取决于程序加载库的顺序)。所以这意味着从技术上讲,由于竞争条件,prefork 处理程序内部可能会发生死锁。

例如,考虑这个序列:

  1. 线程 T1 调用 fork()
  2. 在 T1 中调用 libc prefork 处理程序(例如,T1 现在拥有所有 libc 锁)
  3. 接下来,在线程 T2 中,第 3 方库 A 获取其自己的互斥量 AM,然后进行需要互斥量的 libc 调用。这会阻塞,因为 libc 互斥锁由 T1 持有。
  4. 线程 T1 为库 A 运行 prefork 处理程序,该处理程序阻塞等待获取由 T2 持有的 AM。

有你的死锁,它与你自己的互斥锁或代码无关。

这实际上发生在我曾经参与的一个项目中。我当时找到的建议是选择 fork 或 threads,但不能同时选择两者。但是对于一些可能不实用的应用程序。

原文由 Kevin 发布,翻译遵循 CC BY-SA 4.0 许可协议

只要您 非常 小心 fork 和 exec 之间的代码,在多线程程序中 fork 是安全的。您只能在该范围内进行可重入(也称为异步安全)系统调用。理论上,你不能在那里进行 malloc 或 free ,尽管实际上默认的 Linux 分配器是安全的,并且 Linux 库开始依赖它 最终结果是你 必须 使用默认的分配器。

原文由 Igor Nazarenko 发布,翻译遵循 CC BY-SA 3.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
logo
Stack Overflow 翻译
子站问答
访问
宣传栏