如何在python中使用多处理正确终止子进程

如何在python中使用多处理正确终止子进程,python,multiprocessing,Python,Multiprocessing,我有几个回调函数,我想作为多个进程启动,并通过来自父进程的信号终止它们 我目前的做法是使用multiprocessing.Value创建一个共享的c_bool,并将其设置为True,然后在创建所有进程时将其分发给它们。我的进程都使用共享bool运行while循环,如下所示: while myC_bool: ...keep running... 然后,我可以将bool从父进程切换到False,所有子进程将完成最终循环并退出 很多人都告诉我,并且在文档中读到,在使用多处理时,应该尽量避免使用共享内

我有几个回调函数,我想作为多个进程启动,并通过来自父进程的信号终止它们

我目前的做法是使用
multiprocessing.Value
创建一个共享的c_bool,并将其设置为
True
,然后在创建所有进程时将其分发给它们。我的进程都使用共享bool运行while循环,如下所示:

while myC_bool: ...keep running...
然后,我可以将bool从父进程切换到
False
,所有子进程将完成最终循环并退出

很多人都告诉我,并且在文档中读到,在使用多处理时,应该尽量避免使用共享内存。 我被告知避免这种情况的最好方法是对进程进行后台监控,给它一个定制的信号处理程序,并向它发送一个sigint/sigterm/etc

我的问题是,是否专门使用bool来保持循环的活动性,并且只改变父进程中的循环值,并从多个子进程中读取循环,这是一个合适的解决方案,可以使所有子进程快速安全地终止?我觉得对于所有的孩子来说,只看一个共享的bool比给他们发送x个sigint的开销要少


后台监控是更好的解决方案吗?如果是这样的话,我想得到一些帮助来理解原因。

既然您对修改共享变量的人很小心,那就没问题了

可能有许多不同的解决方案。例如,使用a,并在设置时终止流程。 或者使用
多处理连接
对象(来自管道)。后者可用于父母和子女之间的双向沟通。就像给孩子们一个停止的信号,然后给父母一个确认。

告诉你“不要这样做”的人是错误的。共享内存的要点是在多处理器之间共享内存,这正是您正在做的

你有一个解决方案,1)简单,2)有效。信号/守护进程方法1)非常酷,2)更难正确编码,3)更难理解

我在您的方法中看到的唯一陷阱是,进程可能会从CPU缓存中看到一个过时的bool副本,并且在关闭时会稍微延迟。有很多方法可以刷新缓存,以确保不会发生这种情况,但您可能不需要它们,因为对于大多数应用程序来说,缓存刷新经常自动进行


坚持你的立场。

有很多很好的理由支持你的解决方案:

  • 它比信号更容易思考
  • 它需要处理的跨平台问题更少
  • 您已经有了这样工作的代码
  • 如果您希望在将来添加“优雅关机”机制,则可以很容易地添加该机制
……等等

请记住,除非您能向自己证明,
多处理
和您所关心的每个平台上的底层操作系统原语在不同步的情况下都能正常工作,否则您需要在每次访问共享bool时设置一个
或其他东西。这并不复杂,但是……一旦你做到了这一点,在没有共享bool的情况下使用
事件
可能会更简单

无论如何,如果这些都是你的理由,我会说很好,那样做。但根据你的问题,你实际上选择这个是因为性能:

我觉得对于所有的孩子来说,只看一个共享的bool比给他们发送x个sigint要少

如果这是你的理由,你几乎肯定错了。孩子们每次都必须通过一些循环查看共享bool(并获取共享锁!),而信号只需发送给每个孩子一次。因此,通过这种方式,您的开销几乎肯定会高得多


但实际上,我无法想象每个子进程发送一个信号,甚至每个进程每个循环获取一次进程间锁的开销,在任何有用的程序中都是一个瓶颈,所以…为什么开销在这里一开始就很重要?以最简单的方式做最有意义的事情。

你真的不应该只相信“对于大多数应用程序来说,缓存刷新经常自动发生”。如果您不确定在您关心的每个平台上的应用程序是否经常(或永远)发生这种情况,则需要显式同步。否则,您几乎可以保证您的代码会在第一个大型演示中崩溃,或者您将花费数周的时间追踪只在少数用户机器上发生的Heisenbug(当然,他们将是不知道如何提交有用的bug报告的用户)。感谢您的回复,我不知道你提到的陷阱甚至是我可能遇到的一个复杂问题。看起来我可能还有更多的研究要做。谢谢你的回复,我很感激你对我使用的方法的批评。我忽略了一个事实,即即使它们处于不同的进程中,它们仍然必须获得一个锁才能查看值。我认为避免共享值的原因是因为它使进程更像线程,阻碍了使用多个进程的一些优势,对吗?@LISTERINE:我从来没有这样想过,但是的,这是一个很好的看待它的方式。线程的主要缺点是,共享(可变)值比消息传递更难推理,并且如果不考虑竞争,并发编程就足够难了。CPython特有的缺点是,GIL意味着线程代码本质上是过度同步的,最终几乎是串行的,而不是并行的。显式共享值不像隐式共享那么糟糕,围绕一个值的锁也不像GIL那么糟糕,但它们是朝着线程化的缺点后退的一步。@LISTERINE:…就是这样