作为库开发人员,如何防止用户跨多个线程/进程使用对象?

时间:2018-11-14 07:26:59

标签: python multithreading multiprocessing

我正在为Python构建一个库,并注意到我的一个用户通过在2个进程之间共享对象而错误地使用了对象。

所以,我想用某种方式烘烤,以防止用户这样做。

一种方法是在每个方法调用之前使用装饰器添加一个“检查”,但这会在每个方法调用上增加一些开销。

import multiprocessing
import threading


def is_parent():
    return (
        multiprocessing.current_process().name == "MainProcess"
        and threading.current_thread() == threading.main_thread()
    )


def process_unsafe(fn):
    def wrapper(*args, **kwargs):
        if not is_parent():
            raise RuntimeError("Not allowed!")
        fn(*args, **kwargs)

    return wrapper


class NonProcessSafe:
    @process_unsafe
    def foo(self):
        pass

    @process_unsafe
    def bar(self):
        pass


nps = NonProcessSafe()

nps.foo()
nps.bar()


def child():
    # complains
    nps.foo()


p = multiprocessing.Process(target=child)
p.start()
p.join()

有没有更有效的方法来实现这一目标?

1 个答案:

答案 0 :(得分:0)

正如@timgeb在评论中提到的那样,可能无法消除所有漏洞。此外,我不确定我是否完全了解您的问题以及进程/线程/状态共享模型...

无论如何,至少在处理方面(在Unix / Linux方面),这是一个Ansatz可能会带您到某个地方:

import os

class State:

    def __init__(self):
        self._pid = os.getpid()
        self._state = {}

    def __setitem__(self, key, value):
        if os.getpid() != self._pid:
            raise RuntimeError("this process must not change state")
        self._state[key] = value

State实例会记住创建它的进程的ID,然后可以在所有(相关)方法中对该ID进行检查。当然,这将需要通过其“所有者”过程创建国家,在这种情况下,任何父母或兄弟姐妹过程都不会看到它,并且仅对其本国及其子代可见。

对于线程,请使用threading.get_ident()而不是os.getpid()

如果总是由“主”进程创建状态,然后派生工作人员子进程,则可以(在主进程中)暂时将状态标记为只读,请分叉工作人员子进程,使其获得只读权限-仅复制,然后在主进程切换状态下恢复为读写状态:

class State:
    def __init__(self):
        self._read_only = False
        self._state = {}
    def __setattr__(self, key, value):
        if self._read_only:
            raise RuntimeError("read only!")
        self._state[key] = value

在主要过程中:

def new_worker(fct):
    state = State()
    state._read_only = True
    pid = os.fork()  # or whatever you use to create a new process
    if pid:
        fct()  # state is _read-only=True here in child process
    else:
        state._read_only = False
        state_list.append((pid, state))