Python 在不同模块之间共享队列实例
我是Python新手,我想在不同模块中创建的线程/进程之间创建一个“全局静态变量”,即线程安全和进程安全队列。 我从中了解到,全局变量的概念是使用第三个模块创建的,我将其称为cfg,它定义并初始化我的全局队列。 我在模块之间共享此对象的实例时遇到问题,因为我试图在从cfg模块导入的共享队列上打印repr函数,在导入该函数的其他模块中,这表明它们是不同的实例。似乎每次我尝试导入模块时,都会创建一个新实例并将其传递给导入该模块的模块 Main.py:Python 在不同模块之间共享队列实例,python,multithreading,global,Python,Multithreading,Global,我是Python新手,我想在不同模块中创建的线程/进程之间创建一个“全局静态变量”,即线程安全和进程安全队列。 我从中了解到,全局变量的概念是使用第三个模块创建的,我将其称为cfg,它定义并初始化我的全局队列。 我在模块之间共享此对象的实例时遇到问题,因为我试图在从cfg模块导入的共享队列上打印repr函数,在导入该函数的其他模块中,这表明它们是不同的实例。似乎每次我尝试导入模块时,都会创建一个新实例并将其传递给导入该模块的模块 Main.py: import GatewayManager if
import GatewayManager
if __name__ == '__main__':
GatewayManager.initialize()
doSomething()
GatewayManager.py:
import multiprocessing
import queue
import threading
def initialize():
# Multiprocessing or Threading
global isMonoCPU
isMonoCPU = multiprocessing.cpu_count() == 1
global sharedQueue
sharedQueue = multiprocessing.Queue() if not isMonoCPU else queue.Queue()
print("gateway: ", sharedQueue.__repr__())
otherModules.py:
import GatewayManager
# Some module write on the queue
GatewayManager.sharedQueue.put(variable)
# Some read from the queue
GatewayManager.sharedQueue.get()
print("driver: ", GatewayManager.sharedQueue.__repr__())
一个多处理队列在创建它的进程(我们称之为父进程)和由父进程创建的进程(我们称之为子进程)之间共享
以下是一些没有这种关系的流程的示例:
$ python myprogram.py &
$ python myprogram.py &
外壳是这两个孩子的父母。但是,shell没有创建multiprocessing.Queue,因此这两个子进程不会共享它。相反,他们将各自创建自己的。这可能与他们的孩子共享,但不能与其他人共享
您可以很容易地观察到这种行为:
$ cat queuedemo.py
from time import sleep
from os import getpid
from sys import argv
from multiprocessing import Queue
q = Queue()
if argv[1:]:
q.put(getpid())
sleep(60)
else:
print(getpid(), q.get())
exarkun@baryon:/tmp/queue$ python queuedemo.py foo & python queuedemo.py
[1] 28249
第二个进程从未从队列中读取任何内容。然而,如果你给这两个进程父子关系
$ cat queuedemo.py
from os import getpid
from multiprocessing import Queue
from multiprocessing.process import Process
q = Queue()
q.put(getpid())
def child():
print(getpid(), q.get())
p = Process(target=child)
p.start()
p.join()
exarkun@baryon:/tmp/queue$ python queuedemo.py
(28469, 28467)
exarkun@baryon:/tmp/queue$
请注意,q.get调用成功,放入队列的pid与取出队列的进程的pid不同
在某种程度上,这也必然扩展到具有父-子关系和兄弟关系的进程
因此:
全局文件仅在单个进程中共享
多处理模块提供了一些工具,用于在相互关联的进程之间共享状态。
如果您想在没有这种关系的进程之间共享状态,还有很多其他选项,最好的选项将更多地取决于您必须共享的状态类型和共享模式的外观。您的问题中没有包括这两种情况。这里:
# GatewayManager.py:
...
def initialize():
global sharedQueue
# ...
sharedQueue = multiprocessing.Queue()
# ...
在调用GatewayManager.initialize函数之前,您的GatewayManager模块没有sharedQueue属性。因此,如果任何其他模块在调用GatewayManager.initialize之前尝试使用GatewayManager.sharedQueue,那么您当然会遇到此错误。由于GatewayManager.initialize会在每次调用时盲目地重新绑定sharedQueue,如果您从另一个模块再次调用它,那么您将丢失已创建的队列并获得一个新的队列
您想要的是确保您的sharedqueue被创建一次且仅创建一次,并且无论发生什么情况,它都将被创建。这个解决方案,至少是一个解决方案,但这是一个已知的工作解决方案,它是代理所有GatewayManager.sharedQueue.What access thru函数,这些函数将在需要时负责初始化队列
# gateway_manager.py
class _QueueProxy(object):
def __init__(self):
self._queueimp = None
@property
def _queue(self):
if self._queueimp is None:
isMonoCPU = multiprocessing.cpu_count() == 1
self._queueimp = queue.Queue() if isMonoCPU else multiprocessing.Queue()
return self._queueimp
def get(self, *args, **kw):
return self._queue.get(*args, **kw)
def put(self, *args, **kw):
return self._queue.put(*args, **kw)
# etc... only expose public methods and attributes of course
# and now our `shared_queue` instance
shared_queue = _QueueProxy()
现在,您几乎可以安全地完成队列创建,因为队列创建不是原子的,所以您可能有竞争条件,可以从任何模块使用gateway_manager.shared_队列,而无需关心初始化
当然,如果您有两个不同的进程,我不是说多进程。这里的进程您仍然有两个不同的队列,但我假设您已经理解了这一点,如果不理解,请阅读Jean Paul的回答。如果您想在进程之间共享状态,您需要一个数据库。不一定是关系型的,也不一定是持久型的,比如Redis可能就足够了,但它必须处理并发性。wrt/your每次我尝试导入一个模块时,一个新实例出现问题,没有MCVE就无法回答。我添加了一个MCV示例。我试图在main中创建一个日化函数,但它不起作用,它说没有模块GatewayManager没有属性sharedqueue谢谢你的回答,我理解了你回答的概念。在我的例子中,使用python shell启动的main.py模块是创建其他进程或线程的模块。我认为我的所有进程都是相关的,我可以在它们之间使用sharedQueue。它是如何创建其他进程的?我让它工作了,这些进程是在调用initialize之后创建的,在doSomething函数中,通过创建一个进程实例并在启动进程时传递一个要运行的函数。这是我思考的标准方式,不重写类。我理解你的观点,这就是我创建initialize函数的原因。我在main.py中只调用了一次函数作为第一条语句,至少我是这么认为的。其他进程和通过初始化函数下面的doSomething函数创建并使用sharedQueue。很抱歉,您的MCVE既不是C完整的,也不是V可验证的,我们对程序流等一点也不了解,因此您无法期待更好的答案。
也就是说你的初始化函数是不安全的,你不应该依赖客户端代码来做正确的事情-你的工作就是确保它的安全,在您的otherModule示例中,我们所有的都是顶级语句,因此很明显,如果您在初始化队列之前导入这些模块,它将无法工作。很抱歉,我成功地使其工作,程序还可以,但我认为它没有在队列上看到repr函数的输出:它们有不同的值,但队列是共享的。我认为查看队列实例是否唯一是一个好办法,但事实并非如此。我认为在低级别内存中,根据您分叉进程的上下文,它可以使用父内存的相同内存空间,或者复制到另一个位置,并将这些空间链接在一起。我不确定,但它现在起作用了。