使用multiprocessing.starmap()在进程之间共享计数器

时间:2018-12-04 16:27:33

标签: python python-3.x multiprocessing

我想使用multiprocessing.Value + multiprocessing.Lock在不同的进程之间共享一个计数器。例如:

import itertools as it
import multiprocessing

def func(x, val, lock):
    for i in range(x):
        i ** 2
    with lock:
        val.value += 1
        print('counter incremented to:', val.value)

if __name__ == '__main__':
    v = multiprocessing.Value('i', 0)
    lock = multiprocessing.Lock()

    with multiprocessing.Pool() as pool:
        pool.starmap(func, ((i, v, lock) for i in range(25)))
    print(counter.value())

这将引发以下异常:

  

RuntimeError:同步的对象仅应在以下对象之间共享   通过继承进行处理

我最困惑的是,一个相关的(虽然不是完全相似的)模式适用于multiprocessing.Process()

if __name__ == '__main__':
    v = multiprocessing.Value('i', 0)
    lock = multiprocessing.Lock()

    procs = [multiprocessing.Process(target=func, args=(i, v, lock))
             for i in range(25)]
    for p in procs: p.start()
    for p in procs: p.join()

现在,我认识到这是两个明显不同的东西:

  • 第一个示例使用了多个等于cpu_count()的工作进程,并在它们之间拆分了一个可迭代的range(25)
  • 第二个示例使用一个输入创建25个工作流程和任务

那就是说:如何以这种方式与pool.starmap()(或pool.map())共享一个实例?

我见过类似的问题hereherehere,但是这些方法似乎不适合.map() / .starmap() ,关于Value是否使用ctypes.c_int的看法。


我意识到这种方法在技术上是可行的:

def func(x):
    for i in range(x):
        i ** 2
    with lock:
        v.value += 1
        print('counter incremented to:', v.value)

v = None
lock = None

def set_global_counter_and_lock():
    """Egh ... """
    global v, lock
    if not any((v, lock)):
        v = multiprocessing.Value('i', 0)
        lock = multiprocessing.Lock()

if __name__ == '__main__':
    # Each worker process will call `initializer()` when it starts.
    with multiprocessing.Pool(initializer=set_global_counter_and_lock) as pool:
        pool.map(func, range(25))

这真的是最佳做法吗?

1 个答案:

答案 0 :(得分:1)

使用Cadre时获得的run是因为在通过(内部池)队列发送到工作进程之前,对池方法的参数进行了腌制。 您要使用哪种池方法与此处无关。当您仅使用import java.awt.BorderLayout; import java.awt.EventQueue; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.JTextField; import javax.swing.WindowConstants; public class Test { public static void main(String[] args) { EventQueue.invokeLater(new Cadre()); } public static class Cadre extends JFrame implements Runnable { // private PanneauHaut panneauHaut; // private PanneauBas panneauBas; @Override public void run() { System.out.println("Thread started"); this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); // setExtendedState(JFrame.MAXIMIZED_BOTH); // this.panneauHaut = new PanneauHaut(); // this.panneauBas = new PanneauBas(); JPanel mainPanel = new JPanel(); JTextField kooltxt = new JTextField("hehehe"); mainPanel.setLayout(new BorderLayout()); mainPanel.add(kooltxt); // mainPanel.add(panneauHaut, BorderLayout.NORTH); // mainPanel.add(panneauBas.getPanel(), BorderLayout.SOUTH); this.setContentPane(mainPanel); pack(); setLocationRelativeTo(null); this.setVisible(true); } } } 时不会发生这种情况,因为不涉及队列。您可以仅使用RuntimeError重现该错误。

您的最后一个代码段不符合您的想法。您不是共享一个Pool,而是为每个子进程重新创建独立的计数器。

如果您在Unix上并使用默认的启动方法“ fork”,则只需将共享对象作为参数传递给池方法即可。 您的子进程将通过派生继承全局变量。如果使用流程启动方法“ spawn”(默认Windows)或“ forkserver”,则在Process期间必须使用pickle.dumps(multiprocessing.Value('i', 0)) 实例化,以使子进程继承共享对象。

请注意,这里不需要额外的Value,因为initializer默认带有一个可以使用的内部变量。

Pool