簡體   English   中英

跨多個流程和模塊共享的全局可修改對象

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

目前最好的解決方案是使用線程,但是我更喜歡使用進程。 自然,如果存在任何更簡單(或更優)的解決方案,我很樂意考慮它們。

編輯

我已經意識到我在先前的線程嘗試中打過錯,並且使用多個線程實際上可以很好地工作。 這是一個很棒的課程,可以學習兩次閱讀代碼...

一種方法是使用各種緩存模塊之一。 diskcacheshelve等,都提供持久化對象的能力。 當然, 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的方法ab

您可能也有興趣在SyncManager是的子類BaseManager一個已經注冊了很多基本的類型,包括字典,基本上涵蓋了在這里的功能性。

暫無
暫無

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

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