Python在进程之间共享锁定

DJM*_*y12 41 python share locking multiprocessing

我试图使用部分函数,​​以便pool.map()可以定位具有多个参数的函数(在本例中为Lock()对象).

这是示例代码(取自我之前的一个问题的答案):

from functools import partial

def target(lock, iterable_item):
    for item in items:
        # Do cool stuff
        if (... some condition here ...):
            lock.acquire()
            # Write to stdout or logfile, etc.
            lock.release()

def main():
    iterable = [1, 2, 3, 4, 5]
    pool = multiprocessing.Pool()
    l = multiprocessing.Lock()
    func = partial(target, l)
    pool.map(func, iterable)
    pool.close()
    pool.join()
Run Code Online (Sandbox Code Playgroud)

但是,当我运行此代码时,我收到错误:

Runtime Error: Lock objects should only be shared between processes through inheritance.
Run Code Online (Sandbox Code Playgroud)

我在这里错过了什么?如何在子进程之间共享锁?

dan*_*ano 80

对不起,我应该在回答你的另一个问题时发现这一点.您无法将普通multiprocessing.Lock对象传递给Pool方法,因为它们无法进行pickle.有两种方法可以解决这个问题.一个是创建Manager()并传递Manager.Lock():

def main():
    iterable = [1, 2, 3, 4, 5]
    pool = multiprocessing.Pool()
    m = multiprocessing.Manager()
    l = m.Lock()
    func = partial(target, l)
    pool.map(func, iterable)
    pool.close()
    pool.join()
Run Code Online (Sandbox Code Playgroud)

不过,这有点重量级; 使用a Manager需要生成另一个进程来托管Manager服务器.所有对acquire/ releaselock的调用都必须通过IPC发送到该服务器.

另一种选择是multiprocessing.Lock()使用initializerkwarg 在Pool创建时传递常规.这将使您的锁实例在所有子工作者中全局:

def target(iterable_item):
    for item in items:
        # Do cool stuff
        if (... some condition here ...):
            lock.acquire()
            # Write to stdout or logfile, etc.
            lock.release()
def init(l):
    global lock
    lock = l

def main():
    iterable = [1, 2, 3, 4, 5]
    l = multiprocessing.Lock()
    pool = multiprocessing.Pool(initializer=init, initargs=(l,))
    pool.map(target, iterable)
    pool.close()
    pool.join()
Run Code Online (Sandbox Code Playgroud)

第二种解决方案具有不再需要的副作用partial.

  • @bawejakunal`multiprocessing.Lock`是一个过程安全对象,因此您可以将其直接传递给子进程并在所有进程中安全地使用它.但是,大多数可变的Python对象(如`list`,`dict`,大多数用户创建的类)都不是*进程安全的,因此在进程之间传递它们会导致在每个进程中创建的对象的完全不同的副本.在这些情况下,您需要使用`multiprocessing.Manager`. (4认同)
  • @neilxdims使用`Process`,Lock在子进程分叉时继承,并直接传递给您作为进程目标传递的任何方法.使用`Pool`,当您将参数传递给`map`和`apply`等方法时,进程已经分叉,因此继承将不起作用.然而,`Pool`的`initializer`函数的参数是继承的,因此可以传递`Lock`.但是,对于池进程执行的方法来访问`Lock`,它需要是全局的; 否则它将在`initializer`函数之外无法访问. (3认同)
  • @dano 非常感谢你的回答,我也有同样的问题,这个问题完美地解决了这个问题,但是我有另一个问题,为什么不经常使用这种方法在进程之间共享状态,而不是通过 Manager 对象这样做,它有它自己运行服务器进程和代理访问的开销? (2认同)
  • 你能解释一下为什么在使用 Pool 时需要将 Lock 创建为全局变量,而在使用 Process 时可以作为参数传递吗? (2认同)
  • 为什么不能简单地将锁声明为全局锁并从每个池函数中访问它? (2认同)
  • @pistacchio在Linux平台上,你可以做到这一点.在Windows上,您不能,因为Windows不支持分叉.您将在每个子进程中使用不同的锁定对象. (2认同)