通过全局标志变量进行进程间信令的python多处理是否安全?

通过全局标志变量进行进程间信令的python多处理是否安全?,python,parallel-processing,multiprocessing,global,globals,distributed-systems,Python,Parallel Processing,Multiprocessing,Global,Globals,Distributed Systems,我正在运行许多子进程(比我有更多的内核),如果其中一个子进程满足某个条件,我将设置一个全局变量的值全局紧急援助 然后,如果设置了bailout,则所有后续子流程将尽快退出 例如,请参见这个简单的示例,其中我将对loop()函数的20个调用的结果相乘,但如果其中任何一个调用返回零,我将“退出”: import sys import random import multiprocessing def loop(tup): global bailout if bailout==1:

我正在运行许多子进程(比我有更多的内核),如果其中一个子进程满足某个条件,我将设置一个全局变量的值
全局紧急援助

然后,如果设置了
bailout
,则所有后续子流程将尽快退出

例如,请参见这个简单的示例,其中我将对
loop()
函数的20个调用的结果相乘,但如果其中任何一个调用返回零,我将“退出”:

import sys
import random
import multiprocessing

def loop(tup):
    global bailout
    if bailout==1:                    # obey a global bail out "flag"
        return 0
    x = random.random() - 0.5
    if x < 0:
        bailout = 1                   # set a global bail out "flag"
        return 0
    return x

def top():
    global bailout
    bailout = 0
    runtups = 20 * [[0]]              # a dummy parameter [0] for function "loop"
    pool = multiprocessing.Pool()
    results = pool.imap(loop, runtups)
    pool.close()
    res = 1
    sys.stdout.write("1")
    for result in results:
        sys.stdout.write(" * %g" % result)
        res = res * result
    sys.stdout.write(" = %g\n" % res)

top()
导入系统 随机输入 导入多处理 def循环(tup): 全球救助 如果纾困=1:#遵守全球纾困“标志” 返回0 x=random.random()-0.5 如果x<0: 救助=1#设置全球救助“标志” 返回0 返回x def top(): 全球救助 援助=0 runtups=20*[[0]]#函数“loop”的伪参数[0] 池=多处理。池() 结果=pool.imap(循环,runtups) pool.close() res=1 系统标准写入(“1”) 对于结果中的结果: sys.stdout.write(“*%g”%result) res=res*结果 sys.stdout.write(“=%g\n”%res) top() 它工作得很好(或者准确地说,每次我尝试它时,它都能工作)。i、 e.我的桌面有4个内核,如果前4个子进程中的一个将baild设置为1(在本例中几乎总是这样),那么如果baild==1,则所有后续运行都将在
条件下退出

但它安全吗


我的意思是,一个子流程所能做的就是将
bailout
设置为1。但如果两个子流程都想将紧急援助设置为1怎么办?他们是否有可能同时尝试这样做,导致救助变得不明确?或者可以保证这永远不会发生(可能是因为顶级流程总是以串行方式处理已完成的子流程?

全局变量不会在流程之间共享。如果将一些日志添加到
循环中
,您可以看到实际发生的情况:

def loop(tup):
    global bailout
    if bailout==1:
        print(f'pid {os.getpid()} had bailout 1')
        return 0
    x = random.random() - 0.5
    if x < 0:
        print(f'pid {os.getpid()} setting bailout 1')
        bailout = 1
        return 0
    return x
发生的情况是,
multiprocessing.Pool()
正在启动4个进程,这些进程在可用时会被重新使用。因此,在处理
运行程序中的20个项目时,最终每个进程的
紧急援助设置为1。当这个过程被重复使用时,它就会触发救助条款

由于您随机决定何时设置
bailout=1
,因此在处理这20个项目时,它可能永远不会发生,或者它可能发生在某些流程中,但在其他流程中不会发生,因此您可能不会得到与我上面粘贴的相同的结果,但至少某些流程可能会进入bailout模式

如果您正在寻找一种可靠的方法来在进程之间共享状态,请查看。

可能吗?
安全吗?
有保证吗? 虽然GIL stepping确实使所有基于线程(而不是基于子进程)的多处理工作仍然出现在纯
[SERIAL]
处理流中,但问题更多的是一种主要方法,以及是否安全地满足了上述所有问题


相反,不要试图违背记录在案的建议: 最好让我们提到文档中的明确声明:

显式地将资源传递给子进程

在Unix上,子进程可以使用在使用全局资源的父进程中创建的共享资源。但是,最好将对象作为参数传递给子进程的构造函数

除了使代码(可能)与Windows兼容之外,这还可以确保只要子进程仍处于活动状态,对象就不会在父进程中被垃圾收集。如果在父进程中对对象进行垃圾收集时释放了某些资源,这可能很重要


全局变量

请记住,如果在子进程中运行的代码试图访问全局变量,那么它看到的值(如果有)可能与调用
进程.start时父进程中的值不同

然而,仅仅是模块级常量的全局变量不会引起任何问题



除了帮助交流或“共享”状态的本机pythonic工具(在可能的情况下,我不仅主张更好地不共享),还有智能工具用于设计,使用多代理概念,每个线程可以使用其他轻量级通信工具,性能受到的惩罚更少,比本机GIL步进操作允许的要多(参考ZeroMQ、nanomsg等)。

如果不明显,我所要做的就是从并行循环中“中断”,一旦我知道运行“循环”的其余运行不会影响我的答案。所以真正的问题是,打破并行循环的最佳方式是什么。
pid 30011 setting bailout 1
pid 30013 setting bailout 1
pid 30015 setting bailout 1
pid 30009 setting bailout 1
pid 30010 setting bailout 1
pid 30011 had bailout 1
pid 30013 had bailout 1
pid 30009 had bailout 1
pid 30014 setting bailout 1
pid 30015 had bailout 1
pid 30010 had bailout 1
pid 30011 had bailout 1
1 * 0.494123 * 0.0704172 * 0 * 0.10829 * 0 * 0.465238 * 0 * 0.0638724 * 0 * 0 * 0 * 0.227231 * 0 * 0 * 0 * 0 * 0 * 0 * 0.463628 * 0.372984 = 0