在多处理中创建单例类

Posted

技术标签:

【中文标题】在多处理中创建单例类【英文标题】:Make Singleton class in Multiprocessing 【发布时间】:2017-12-18 00:35:27 【问题描述】:

我使用 Metaclass 创建 Singleton 类,它在多线程中运行良好并且只创建 MySingleton 类的一个实例,但在多处理中,它总是创建新实例

import multiprocessing

class SingletonType(type):
    # meta class for making a class singleton
    def __call__(cls, *args, **kwargs):

        try:
            return cls.__instance
        except AttributeError:
            cls.__instance = super(SingletonType, cls).__call__(*args, **kwargs)
            return cls.__instance

class MySingleton(object):
    # singleton class
    __metaclass__ = SingletonType

    def __init__(*args,**kwargs):
        print "init called"


def task():
    # create singleton class instance
    a = MySingleton()


# create two process
pro_1 = multiprocessing.Process(target=task)
pro_2 = multiprocessing.Process(target=task)

# start process
pro_1.start()
pro_2.start()

我的输出:

init called
init called

我需要 MySingleton 类 init 方法只被调用一次

【问题讨论】:

这里为什么需要元类? 我在上面的代码中使用元类制作单例类我创建“SingletonType”元类,MySingleton 是单例类 与线程不同的进程不共享上下文(它们在分叉时都会创建它的副本)。如果你想在进程之间交换数据,你应该在这里查看 IPC。 本机对象不会在进程之间共享(由于引用计数)。 @TomWyllie:不是第一次想要单例的人们提出元类。一些来自另一种语言的人可能写了一个流行的文档来暗示这种模式。我通常不赞成打人,但在这种情况下,我想知道...... 【参考方案1】:

您的每个子进程都运行自己的 Python 解释器实例,因此一个进程中的 SingletonType 不会与另一个进程中的那些共享其状态。这意味着只存在于您的一个进程中的真正单例将几乎没有用处,因为您将无法在其他进程中使用它:虽然您可以manually share data between processes,但仅限于基本数据类型(例如字典和列表)。

不要依赖单例,只需在进程之间共享底层数据:

#!/usr/bin/env python3

import multiprocessing
import os


def log(s):
    print(': '.format(os.getpid(), s))


class PseudoSingleton(object):

    def __init__(*args,**kwargs):
        if not shared_state:
            log('Initializating shared state')
            with shared_state_lock:
                shared_state['x'] = 1
                shared_state['y'] = 2
            log('Shared state initialized')
        else:
            log('Shared state was already initalized: '.format(shared_state))


def task():
    a = PseudoSingleton()


if __name__ == '__main__':
    # We need the __main__ guard so that this part is only executed in
    # the parent

    log('Communication setup')
    shared_state = multiprocessing.Manager().dict()
    shared_state_lock = multiprocessing.Lock()

    # create two process
    log('Start child processes')
    pro_1 = multiprocessing.Process(target=task)
    pro_2 = multiprocessing.Process(target=task)
    pro_1.start()
    pro_2.start()

    # Wait until processes have finished
    # See https://***.com/a/25456494/857390
    log('Wait for children')
    pro_1.join()
    pro_2.join()

    log('Done')

打印出来

16194: Communication setup
16194: Start child processes
16194: Wait for children
16200: Initializating shared state
16200: Shared state initialized
16201: Shared state was already initalized: 'x': 1, 'y': 2
16194: Done

但是,根据您的问题设置,使用其他进程间通信机制可能会有更好的解决方案。例如,Queue class 通常非常有用。

【讨论】:

但我创建了 PseudoSingleton 类“a”和“b”的两个实例并尝试打印 a is b 它返回 False,这意味着 PseudoSingleton 不完全是 Singleton 类我总是得到新实例 正如我所说,您不能在进程之间共享对象实例。这意味着您将在每个子进程中至少有一个单独的类实例。这些不能共享它们的内部状态(它们的属性),但它们可以使用单独定义的共享状态(在我的示例中为shared_state)。这样一来,您的实例将不是真正的单例,但仍会表现得像一个(因为它们在相同的共享数据上运行)。 我可以重新初始化 PseudoSingleton 类的所有变量,意思是我可以手动设置或重置 shared_state_lock 锁

以上是关于在多处理中创建单例类的主要内容,如果未能解决你的问题,请参考以下文章

如何在分布式环境中处理单例类

Kevin Learn Kotlin:数据类单例类枚举类

请问java 单例类 与 静态类 有何不同?

Python单例模式的实现方式

Java单例模式详解

使用python中的构造函数限制在单例类中创建对象