繁体   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