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

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了python多处理是否通过全局标志变量安全地进行进程间信令?相关的知识,希望对你有一定的参考价值。

我正在运行许多子进程(比我有核心),如果一个满足某个条件,我设置一个全局变量global bailout的值。

然后,如果设置了bailout,则所有后续子进程都会尽快退出。

参见例如这个简单的例子,我将我的20个调用的结果乘以loop()函数,但如果这些调用中的任何一个返回零,我会“拯救”:

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
" % res)

top()

它工作正常(或者确切地说,每次我尝试它都有效)。即我的桌面有4个核心,如果前4个子进程中的一个将救助设置为1(在本例中几乎总是如此),那么所有后续运行都会在if bailout==1条件下退出。

但它安全吗?

我的意思是,所有子进程都可以将bailout设置为1.但是,如果两个子进程都想要将bailout设置为1怎么办?他们有可能同时尝试,导致救助变得不确定吗?或者保证这种情况永远不会发生(也许是因为顶级进程总是串行处理已完成的子进程?)

答案

全局不在进程之间共享。如果你向loop添加一些日志记录,你可以看到真正发生的事情:

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

这将产生如下输出:

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

正在发生的事情是multiprocessing.Pool()正在启动4个进程,这些进程在可用时重新使用。因此,在处理runtups中的20个项目时,最终每个单独的进程都将其bailout设置为1.当重用该进程时,它会触发救助条款。

由于你是在随机决定何时设置bailout = 1,它可能在处理20个项目时永远不会发生,或者它可能发生在某些进程中但不会发生在其他进程中,所以你可能无法获得我上面粘贴的相同结果,但至少一些流程很可能会进入救助模式。

如果您正在寻找一种在流程之间共享状态的可靠方法,请查看https://docs.python.org/3/library/multiprocessing.html#sharing-state-between-processes

另一答案

可能吗? 安全吗? 有保证吗?

虽然GIL步进确实使得所有基于线程(而不是基于子进程的)多处理工作仍然出现在纯[SERIAL]处理流程中,但问题更多的是关于主要方法以及是否所有上述问题都安全地得到满足。


相反,不要试图违背记录的建议:

最好让我们提一下文档中的明确声明:

16.6.3. Programming guidelines

...

明确地将资源传递给子进程

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

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


16.6.3.2 Windows

...

全局变量

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

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


除了本地pythonic工具,以帮助沟通或“共享”状态(这不仅是我提倡,在可能的情况下,更好地永远不分享),有智能工具设计确实,使用多代理概念,其中每个线程可能使用其他轻量级通信工具,性能较低,比原生GIL步进操作允许(参见ZeroMQ,nanomsg等)。

以上是关于python多处理是否通过全局标志变量安全地进行进程间信令?的主要内容,如果未能解决你的问题,请参考以下文章

Freertos-事件标志组,消息队列,信号量,二值信号量,互斥信号量

python中的多处理模块和修改共享的全局变量

stm32数组越界一定会进硬件错误中断吗

Python多处理安全地写入文件

python多处理子进程无法访问全局变量

如何在 Python 中的多处理期间访问全局变量 [重复]