跨进程共享多处理同步原语

时间:2015-04-02 22:10:34

标签: python python-3.x multiprocessing python-multiprocessing

(Python 3.4,Linux)。

我有一个主流程' P',它分叉8个流程(' C1'到' C8')。我想创建multiprocessing.Barrier,以确保所有8个子进程在某一点同步。

如果我在父进程中定义同步原语,那么一切正常,所以当我分叉子进程时,它被正确地继承:

import multiprocessing as mp
barrier = mp.Barrier(8)

def f():
  # do something
  barrier.wait()
  # do more stuff

def main():
  for i in range(8):
    p = mp.Process(target = f)
    p.start()

if __name__ == '__main__':
  main()

但在我的情况下,我不知道创建Barrier对象所需的详细信息,直到子进程开始之后(我不知道我要传递的参数为action参数)。因此,我想在其中一个子进程中创建Barrier,但我不知道如何将其提供给其他子进程。以下工作当然不会起作用,因为子进程中的8个Barrier对象完全相互独立:

import multiprocessing as mp

def f():
  global barrier
  # do something
  barrier = mp.Barrier(8)
  barrier.wait()
  # do more stuff

def main():
  for i in range(8):
    p = mp.Process(target = f)
    p.start()

if __name__ == '__main__':
  main()

我正在考虑在其中一个子流程中创建barrier,然后使用multiprocessing.Queue将其传递给其他子流程(或者如果Queue不接受Barrier对象,使用multiprocessing.Manager().Barrier)。但是,即使这样可行,我也不知道如何确保只有一个进程实际put个(7个副本)同步原语进入队列,而其他进程只有get个。 (当然,我可以在父进程中创建另一个同步原语只是为了做到这一点,但我不得不重构我的代码,以便在父进程中创建原始的Barrier。)

3 个答案:

答案 0 :(得分:1)

以下是一个示例,说明如何通过在一个孩子中创建multiprocessing.managers.BaseManager,然后从所有其他孩子连接到该经理来实现此目的。请注意,它需要将multiprocessing.Lock从父级传递给所有子级以用于同步目的,您提到的是您希望避免这种情况。不过,我不确定是否有其他选择。

import multiprocessing as mp
from multiprocessing.managers import BaseManager

class MyManager(BaseManager):
    pass

def f(lock):
  # do something
  with lock:
      try:
          MyManager.register('get_barrier')
          m = MyManager(address=('localhost', 5555), authkey=b'akey')
          m.connect()
          b = m.get_barrier()
          print("Got the barrier from the manager")
      except OSError as e:
          # We are the first. Create the manager, register
          # a mp.Barrier instance with it, and start it up.
          print("Creating the manager...")
          b = mp.Barrier(8)
          MyManager.register('get_barrier', callable=lambda:b)
          m = MyManager(address=('localhost', 5555), authkey=b'akey')
          m.start()
  b.wait()
  print("Done!")
  # do more stuff

def main():
    lock = mp.Lock()
    for i in range(8):
        p = mp.Process(target=f, args=(lock,))
        p.start()

if __name__ == '__main__':
  main()

输出:

Creating the manager...
Got the barrier from the manager
Got the barrier from the manager
Got the barrier from the manager
Got the barrier from the manager
Got the barrier from the manager
Got the barrier from the manager
Got the barrier from the manager
Done!
Done!
Done!
Done!
Done!
Done!
Done!
Done!

答案 1 :(得分:1)

是否可以简单地捕获流程的ID并仅在其中一个中手动调用您的操作?像这样的东西?

import multiprocessing as mp
barrier = mp.Barrier(8)

def f():
  # create action
  def action():
      print("action was run on process {}.".format(id))

  # do something
  print("Hello from process {}.".format(id))
  id = barrier.wait()
  if id == 0:
      action()
  barrier.wait()

  # Do more stuff

def main():
  for i in range(8):
    p = mp.Process(target = f)
    p.start()

if __name__ == '__main__':
  main()

答案 2 :(得分:0)

这是一个也可以在Windows上使用的版本(缺少的fork会引起其他问题):

import multiprocessing as mp

def procs(uid_barrier):
    uid, barrier = uid_barrier
    print(uid, 'waiting')
    barrier.wait()
    print(uid, 'past barrier')    

def main():
    N_PROCS = 10
    with mp.Manager() as man:
        barrier = man.Barrier(N_PROCS)
        with mp.Pool(N_PROCS) as p:
            p.map(procs, ((uid, barrier) for uid in range(N_PROCS)))

if __name__ == '__main__':
    mp.freeze_support()
    main()