簡體   English   中英

在不同模塊之間共享一個隊列實例

[英]Sharing a Queue instance between different modules

我是 Python 的新手,我想在不同模塊中創建的線程/進程之間創建什么是“全局靜態變量”,我的線程安全和進程安全隊列。 我從文檔中讀到全局變量的概念是使用第三個模塊創建的,我將其稱為 cfg,它定義並初始化我的全局隊列。 我在模塊之間共享此對象的實例時遇到問題,因為我試圖在從 cfg 模塊導入的共享隊列上打印repr () 函數,在導入它的其他模塊中,它表明它們是不同的實例。 似乎每次我嘗試導入一個模塊時都會創建一個新實例並將其傳遞給導入它的模塊。

主要.py:

import GatewayManager
if __name__ == '__main__':
    GatewayManager.initialize()
    doSomething()

網關管理器.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__())

其他模塊.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__())

multiprocessing.Queue在創建它的進程(我們稱之為“Parent”)和由 Parent 創建的進程(我們稱之為“Children”)之間共享。

下面是一些沒有這種關系的進程的例子:

$ 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 不同。

有些必然,這也擴展到具有父子關系和兄弟關系的過程。

所以:

  • 全局變量僅在單個進程中共享
  • multiprocessing 模塊提供了在彼此正確關聯的進程之間共享狀態的工具。

如果您想在沒有這種關系的進程之間共享狀態,還有多種其他選擇 - 最好的選擇更多地取決於您必須共享的狀態類型以及您的共享模式是什么樣的(您都不是已包含在您的問題中)。

這里:

# GatewayManager.py:

...

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

您的GatewayManager模塊在調用initialize()函數之前沒有sharedQueue屬性。因此,如果任何其他模塊在GatewayManager.initialize()之前嘗試使用GatewayManager.sharedQueue那么您當然會收到此錯誤。 由於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而不必關心初始化。

當然,如果您有兩個不同的進程(我不是在這里談論multiprocessing.Process ),您仍然會有兩個不同的隊列,但我假設您已經理解了這一點(如果沒有,請閱讀 Jean-Paul 的回答)。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM