[英]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.