通过全局标志变量进行进程间信令的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