首页 > 解决方案 > 跨多个进程和模块共享的全局可修改对象

问题描述

问题

我正在编写一个软件,我想在其中共享某个模块的对象。这个对象应该可以从不同的模块和不同的进程中修改。考虑以下(简化)版本的问题:

模块

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因为每个进程都有自己的内存空间。最初我尝试使用Managerfrommultiprocessing模块修复它,但是,我未能设置它,我认为是由于导入语句的执行时间和方式出现错误。这是调用's时Manager()的错误消息:Shared__init__

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.

目前最好的解决方案是使用线程,但是我更喜欢使用进程。当然,如果存在任何更简单(或更好)的解决方案,我会很乐意考虑它们。

编辑

我意识到我在之前的线程尝试中犯了一个错字,并且使用多个线程实际上工作得很好。学习阅读你的代码两次的一个很好的教训......

标签: pythonconcurrencyscopemultiprocessing

解决方案


一种方法是使用各种缓存模块之一。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这里实现了非常简单的功能来演示功能。请务必阅读清晰简洁的文档,以便更好地理解。

另外,我的输出是一个无序的字典。您可以轻松地产生排序以module_a始终首先匹配您自己的输出。为简单起见,我省略了这一点。


推荐阅读