簡體   English   中英

跨進程傳遞隊列中的對象引用

[英]Passing object references in a queue across processes

我有幾個multiprocessing.Process es並希望它們使用(隊列get() )可調用的非可選對象並調用它們。 這些是在fork()之前創建的,因此它們不需要酸洗。

使用multiprocessing.Queue不起作用,因為它試圖腌制所有東西:

import multiprocessing as mp

# create non-global callable to make it unpicklable
def make_callable():
    def foo():
        print("running foo")
    return foo

def bar():
    print("running bar")

def runall(q):
    while True:
        c = q.get()
        if c is None:
            break
        c()

if __name__ == '__main__':
    q = mp.Queue()
    call = make_callable()
    p = mp.Process(target=runall, args=(q,))
    p.start()
    q.put(bar)
    q.put(call)
    q.put(None)
    p.join()
running bar
Traceback (most recent call last):
  File "/usr/lib64/python3.7/multiprocessing/queues.py", line 236, in _feed
    obj = _ForkingPickler.dumps(obj)
  File "/usr/lib64/python3.7/multiprocessing/reduction.py", line 51, in dumps
    cls(buf, protocol).dump(obj)
AttributeError: Can't pickle local object 'make_callable.<locals>.foo'

等效的實現是將所有對象放入全局(或傳遞)列表並僅傳遞索引,這些索引有效:

import multiprocessing as mp

# create non-global callable to make it unpicklable
def make_callable():
    def foo():
        print("running foo")
    return foo

def bar():
    print("running bar")

def runall(q, everything):
    while True:
        c = q.get()
        if c is None:
            break
        everything[c]()

if __name__ == '__main__':
    q = mp.Queue()
    call = make_callable()
    everything = [bar, call]
    p = mp.Process(target=runall, args=(q,everything))
    p.start()
    q.put(0)
    q.put(1)
    q.put(None)
    p.join()
running bar
running foo

問題是,雖然我知道傳遞的所有可用的密碼都不會被垃圾收集(因此它們的地址將保持有效),但我沒有事先獲得完整列表。

我也知道我可能使用multiprocessing.Manager及其使用Proxy對象的Queue實現,但這似乎是很多開銷,特別是在實際實現中我也會傳遞其他可選數據。

有沒有辦法挑選並傳遞對象的地址引用,在多個進程之間共享?

謝謝!

確實,Process的目標對象必須是可選擇的。

請注意,函數(內置和用戶定義)由“完全限定”的名稱引用而不是值進行選擇。這意味着只有函數名稱被腌制,以及定義函數的模塊的名稱。函數的代碼,也沒有任何函數屬性被pickle。 因此,定義模塊必須可以在unpickling環境中導入,並且模塊必須包含命名對象,否則將引發異常。

必須在模塊的頂層定義可選擇的函數和類。

因此,在您的情況下,您需要繼續傳遞頂級callables,但在關鍵的runall函數中應用其他檢查/變通辦法:

import multiprocessing as mp

# create non-global callable to make it unpicklable
def make_callable():
    def foo():
        print("running foo")
    return foo

def bar():
    print("running bar")

def runall(q):
    while True:
        c = q.get()
        if c is None:
            break

        res = c()
        if callable(res): res()


if __name__ == '__main__':
    q = mp.Queue()
    p = mp.Process(target=runall, args=(q,))
    p.start()

    q.put(bar)
    q.put(make_callable)
    q.put(None)

    p.join()
    q.close() 

輸出:

running bar
running foo

經過一番思考和搜索,我相信我得到了我想要的答案,主要來自: 通過id()獲取對象?

我可以傳遞可調用的id()然后在生成的進程中將其轉換回來:

import ctypes
a = "hello world"
print ctypes.cast(id(a), ctypes.py_object).value

或者使用gc模塊,只要我保持對對象的引用,這也應該有效:

import gc

def objects_by_id(id_):
    for obj in gc.get_objects():
        if id(obj) == id_:
            return obj
    raise Exception("No found")

然而,這些都不是非常干凈,最后,可能值得限制所有的callables首先和只是傳遞索引。

暫無
暫無

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

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