[英]Globally modifiable object shared across multiple processes and modules
我正在寫一個軟件,我想在其中共享某個模塊中的對象。 該對象應該可以在不同的模塊中以及在不同的過程中進行修改。 考慮以下(簡化)版本的問題:
module_shared.py
# Example class with simplified behaviour
class Shared:
def __init__(self):
self.shared = dict()
def set(self, **kwargs):
for key, value in kwargs.items():
self.shared[key] = value
def get(self, *args):
return {key: self.shared[key] for key in args} if args else self.shared
# Module-scope instance of the Shared class
shared = Shared()
module_a.py
from multiprocessing import Process
from time import sleep
import module_shared as ms
def run():
Process(target=run_process).start()
def run_process():
i = 0
while True:
sleep(3)
ms.shared.set(module_a=i)
i+=1
print("Shared from within module_a", ms.shared.get())
module_b.py
from multiprocessing import Process
from time import sleep
import module_shared as ms
def run():
Process(target=run_process).start()
def run_process():
i = 0
while True:
sleep(2)
ms.shared.set(module_b=i)
i-=1
print("Shared from within module_b", ms.shared.get())
module_main.py
import module_a
import module_b
import module_shared as ms
from time import sleep
if __name__ == '__main__':
module_a.run()
module_b.run()
while True:
sleep(5)
print("Shared from within module_main", ms.shared.get())
正在運行的module_main
的輸出如下:
Shared from within module_b {'module_b': 0}
Shared from within module_a {'module_a': 0}
Shared from within module_b {'module_b': -1}
Shared from within module_main {}
Shared from within module_a {'module_a': 1}
Shared from within module_b {'module_b': -2}
...
預期輸出如下:
Shared from within module_b {'module_b': 0}
Shared from within module_a {'module_a': 0, 'module_b': 0}
Shared from within module_b {'module_a': 0, 'module_b': -1}
Shared from within module_main {'module_a': 0, 'module_b': -1}
Shared from within module_a {'module_a': 1, 'module_b': -1}
Shared from within module_b {'module_a': 1, 'module_b': -2}
...
shared
實例不會全局修改,因為每個進程都有自己的內存空間。 最初,我嘗試使用來自multiprocessing
模塊的Manager
對其進行修復,但是由於無法確定何時以及如何執行import語句,因此我無法對其進行設置。 這是在Shared
的__init__
調用Manager()
時的錯誤消息:
RuntimeError:
An attempt has been made to start a new process before the
current process has finished its bootstrapping phase.
This probably means that you are not using fork to start your
child processes and you have forgotten to use the proper idiom
in the main module:
if __name__ == '__main__':
freeze_support()
...
The "freeze_support()" line can be omitted if the program
is not going to be frozen to produce an executable.
目前最好的解決方案是使用線程,但是我更喜歡使用進程。 自然,如果存在任何更簡單(或更優)的解決方案,我很樂意考慮它們。
編輯 :
我已經意識到我在先前的線程嘗試中打過錯,並且使用多個線程實際上可以很好地工作。 這是一個很棒的課程,可以學習兩次閱讀代碼...
一種方法是使用各種緩存模塊之一。 diskcache
, shelve
等,都提供持久化對象的能力。 當然, pickle
。
例如,使用diskcache庫,您可以采用這種方法,將module_shared.py
替換為:
### DISKCACHE Example ###
from diskcache import Cache
cache = Cache('test_cache.cache')
# Example class with simplified behaviour
class Shared:
def __init__(self, cache):
self.cache = cache
self.cache.clear()
def set(self, **kwargs):
for key, value in kwargs.items():
cache.set(key, value)
def get(self, *args):
return {key: cache.get(key) for key in args} if args else {(key, cache.get(key)) for key in cache.iterkeys()}
# Module-scope instance of the Shared class
shared = Shared(cache)
輸出:
Shared from within module_b {('module_b', 0)}
Shared from within module_a {('module_a', 0), ('module_b', 0)}
Shared from within module_b {('module_a', 0), ('module_b', -1)}
Shared from within module_main {('module_a', 0), ('module_b', -1)}
Shared from within module_a {('module_b', -1), ('module_a', 1)}
Shared from within module_b {('module_b', -2), ('module_a', 1)}
在上面的示例中, module_shared.py
是唯一更改的文件。
各種持久性庫/方法都具有自己的怪癖和功能。 如果您絕對需要將整個類實例對象持久化,那就在那里。 :)性能僅取決於您的工作方式和緩存機制的選擇。 diskcache
證明, diskcache
非常適合我。
我在這里非常簡單地實現了diskcache
,以演示其功能。 請務必閱讀簡潔明了的文檔,以便更好地理解。
另外,我的輸出顯示了一個無序的字典。 您可以輕松地產生已排序的內容,以module_a
始終將您自己的輸出與module_a
匹配。 為了簡單起見,我將其省略。
查看自定義Manager
對象的文檔 ,這是一個想法。
將這些行添加到module_shared.py
:
from multiprocessing.managers import BaseManager
class SharedManager(BaseManager):
pass
SharedManager.register('Shared', Shared)
manager = SharedManager()
manager.start()
shared = manager.Shared()
(擺脫shared
的舊定義)
在我的電腦上運行
$ python module_main.py
Shared from within module_b {'module_b': 0}
Shared from within module_a {'module_b': 0, 'module_a': 0}
Shared from within module_b {'module_b': -1, 'module_a': 0}
Shared from within module_main {'module_b': -1, 'module_a': 0}
Shared from within module_a {'module_b': -1, 'module_a': 1}
Shared from within module_b {'module_b': -2, 'module_a': 1}
Shared from within module_b {'module_b': -3, 'module_a': 1}
Shared from within module_a {'module_b': -3, 'module_a': 2}
Shared from within module_main {'module_b': -3, 'module_a': 2}
Shared from within module_b {'module_b': -4, 'module_a': 2}
...etc
在我看來,這是預期的結果。
奇怪的是, module_shared.py
啟動了一個進程(line manager.start()
),因為我們通常不希望模塊做任何事情,但是由於問題的限制,我認為這是唯一的方法。 如果我是為自己編寫的,那么我module_shared
與此處相同的方式使module_main
的管理器而不是module_shared
(也許使用上面文檔鏈接中描述的上下文管理器,而不是.start
方法),並且我將傳遞該管理器作為函數參數來run
的方法a
和b
。
您可能也有興趣在SyncManager
是的子類BaseManager
一個已經注冊了很多基本的類型,包括字典,基本上涵蓋了在這里的功能性。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.