多线程 fork()
Multi-threaded fork()
在多线程应用中,如果一个线程调用fork(),它只会复制那个线程的状态。所以创建的子进程将是一个单线程进程。如果某个其他线程持有调用 fork() 的线程所需的锁,则该锁将永远不会在子进程中释放。这是个问题。
为了解决这个问题,我们可以通过两种方式修改 fork()。要么我们可以复制所有线程,而不是只复制一个线程。或者我们可以确保(其他)非复制线程持有的任何锁都将被释放。那么在这两种情况下修改后的 fork() 系统调用是什么。这两个哪个更好,或者这两个选项的优点和缺点是什么?
这是一个棘手的问题。
POSIX 有 pthread_atfork()
来处理混合分叉和线程创建的混乱情况。该手册页的注释部分讨论了互斥锁等。但是,它承认很难做到正确。
该函数与其说是 fork()
的替代品,不如说是一种向 pthread 库解释程序需要如何为使用 fork()
做准备的方法。
一般来说,不要尝试从 fork 的子进程启动线程,而是退出该子进程或尽快调用 exec,这样可以最大限度地减少问题。
这个post有一个good discussion of pthread_atfork()
。
...Or we can make sure that any lock held by the (other) non-copied threads will be released.
这比你想象的要难,因为程序可以完全在用户模式代码中实现“锁”,在这种情况下,OS 将不知道它们。
即使您只小心使用 OS 已知的锁,您仍然会遇到一个更普遍的问题:仅使用一个线程创建一个新进程会 有效 与创建一个包含所有线程的新进程然后立即 杀死 除了其中一个以外的所有线程没有什么不同。
了解我们为什么不终止线程。简而言之:锁不是唯一需要清理的状态。在调用 fork 时,任何存在于父线程中但不存在于子线程中的线程都可能正在制造需要清理的混乱。如果子线程中不存在该线程,那么您就不知道需要清理什么。
we can copy all the threads instead of only that single one...
这也是一个潜在的问题。调用 fork()
的一个线程会知道何时以及为什么调用 fork()
,并且它将 准备好 用于 fork 调用。 None 的其他线程会有任何警告。而且,如果这些线程中的任何一个正在与进程 外部 进行交互(例如,与远程服务对话),那么,在您之前有一个客户端与该服务对话的地方,您突然有了两个 客户,与同一个服务交谈,他们都认为他们是唯一的。那不会有好下场的。
不要从多线程程序中调用 fork()
。
在我参与的一个项目中:我们有一个大型多线程程序,需要生成其他进程。我们是如何做到的,我们让它在创建任何新线程之前生成一个简单的单线程“帮助程序”程序。然后,每当它需要生成另一个进程时,它就会向助手发送一条消息,而助手会执行此操作。
在多线程应用中,如果一个线程调用fork(),它只会复制那个线程的状态。所以创建的子进程将是一个单线程进程。如果某个其他线程持有调用 fork() 的线程所需的锁,则该锁将永远不会在子进程中释放。这是个问题。 为了解决这个问题,我们可以通过两种方式修改 fork()。要么我们可以复制所有线程,而不是只复制一个线程。或者我们可以确保(其他)非复制线程持有的任何锁都将被释放。那么在这两种情况下修改后的 fork() 系统调用是什么。这两个哪个更好,或者这两个选项的优点和缺点是什么?
这是一个棘手的问题。
POSIX 有 pthread_atfork()
来处理混合分叉和线程创建的混乱情况。该手册页的注释部分讨论了互斥锁等。但是,它承认很难做到正确。
该函数与其说是 fork()
的替代品,不如说是一种向 pthread 库解释程序需要如何为使用 fork()
做准备的方法。
一般来说,不要尝试从 fork 的子进程启动线程,而是退出该子进程或尽快调用 exec,这样可以最大限度地减少问题。
这个post有一个good discussion of pthread_atfork()
。
...Or we can make sure that any lock held by the (other) non-copied threads will be released.
这比你想象的要难,因为程序可以完全在用户模式代码中实现“锁”,在这种情况下,OS 将不知道它们。
即使您只小心使用 OS 已知的锁,您仍然会遇到一个更普遍的问题:仅使用一个线程创建一个新进程会 有效 与创建一个包含所有线程的新进程然后立即 杀死 除了其中一个以外的所有线程没有什么不同。
了解我们为什么不终止线程。简而言之:锁不是唯一需要清理的状态。在调用 fork 时,任何存在于父线程中但不存在于子线程中的线程都可能正在制造需要清理的混乱。如果子线程中不存在该线程,那么您就不知道需要清理什么。
we can copy all the threads instead of only that single one...
这也是一个潜在的问题。调用 fork()
的一个线程会知道何时以及为什么调用 fork()
,并且它将 准备好 用于 fork 调用。 None 的其他线程会有任何警告。而且,如果这些线程中的任何一个正在与进程 外部 进行交互(例如,与远程服务对话),那么,在您之前有一个客户端与该服务对话的地方,您突然有了两个 客户,与同一个服务交谈,他们都认为他们是唯一的。那不会有好下场的。
不要从多线程程序中调用 fork()
。
在我参与的一个项目中:我们有一个大型多线程程序,需要生成其他进程。我们是如何做到的,我们让它在创建任何新线程之前生成一个简单的单线程“帮助程序”程序。然后,每当它需要生成另一个进程时,它就会向助手发送一条消息,而助手会执行此操作。