在不同模块之间共享队列实例

时间:2018-02-09 12:04:04

标签: python multithreading global

我是Python的新手,我想在不同模块中创建的线程/进程之间创建一个'全局静态变量',我的线程安全和进程安全队列。 我从doc读到全局变量的概念是使用第三个模块创建的,我将其称为cfg,它定义并初始化我的全局队列。 我在模块之间共享此对象的实例时遇到问题,因为我尝试在从cfg模块导入的共享队列上打印 repr ()函数,在导入它的其他模块中,以及它表明它们是不同的实例。似乎每次我尝试导入模块时都会创建一个新实例并将其传递给导入它的模块。

Main.py:

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__())

2 个答案:

答案 0 :(得分:0)

multiprocessing.Queue在创建它的进程(让我们称之为" Parent")和由Parent创建的进程(让他们调用它们&#34)之间共享;儿童"。)

以下是一些没有此关系的流程示例:

$ python myprogram.py &
$ python myprogram.py &

shell是这两个孩子的父母。但是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不同。

有些必然,这也扩展到具有Parent-Descendant和Sibling关系的进程。

所以:

  • 全球只能在一个流程中共享
  • 多处理模块提供了在彼此正确关联的进程之间共享状态的工具。

如果你想在没有这种关系的进程之间共享状态,那么还有其他各种选择 - 最好的选择将更多地取决于你必须分享什么样的状态以及你的共享模式是什么样的(两者都不是)您已将其纳入问题中。)

答案 1 :(得分:0)

下面:

# GatewayManager.py:

...

def initialize():
    global sharedQueue
    # ...
    sharedQueue = multiprocessing.Queue()
    # ...

您的GatewayManager模块在​​调用sharedQueue函数之前没有initialize()属性。所以如果任何其他模块尝试使用{{1}在调用GatewayManager.sharedQueue之前,当然您会收到此错误。由于GatewayManager.initialize()在每次调用时盲目地重新绑定GatewayManager.initialize(),如果再次从另一个模块调用它,那么您将丢失已创建的队列并获得一个新队列。

你想要的是确保你的共享队列只创建一次,并且无论发生什么,它都会被创建。解决方案(好吧,至少一个解决方案 - 但它是一个已知的工作解决方案)这里是代理所有sharedQueue访问通过函数,这些函数将在需要时负责初始化队列。

GatewayManager.sharedQueue.whatever

现在你可以安全地(差不多 - 队列创建不是原子的,所以你可能有竞争条件)从任何模块使用# 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_queue),你仍然会有两个不同的队列,但我认为你已经理解了这一点(如果没有,请阅读Jean-Paul& #39;答案)。