multiprocessing.Pool在Windows正常但在Ubuntu不行?

5 投票
2 回答
6799 浏览
提问于 2025-04-16 22:44

解决了:问题出在Wingware Python IDE上。我想现在自然会有人问,这怎么可能,以及如何解决这个问题。

我昨天问了一个问题( Python中的multiprocessing.Pool问题),这个问题几乎是一样的,但我发现它在Windows电脑上能正常工作,而在我的Ubuntu上却不行。在这篇文章的最后,我会发布一个稍微不同的代码版本,它能做同样的事情。

我的问题简要总结:在Python中使用multiprocessing.Pool时,我并不总是能得到我请求的工作进程数量。当这种情况发生时,程序就会卡住。

我整整一天都在寻找解决方案,然后我想到了诺亚在我之前问题下的评论。他说他的机器上能正常工作,所以我把代码给了我的同事,他用的是Windows机器,运行的是Enthought的64位Python 2.7.1版本。我也用的是同样的版本,但我的机器是在Ubuntu上运行的。我还提到我们俩都在用Wingware Python IDE,但我怀疑这是否重要?

我的代码中有两个问题,而这些问题在我同事的机器上并不存在。

  1. 我并不总是能得到我请求的四个工作进程(尽管我的机器有12个工作进程)。当这种情况发生时,进程就会卡住,不会继续。没有抛出任何异常或错误。

  2. 当我能得到我请求的四个工作进程时(大约每五次中有一次),生成的数字(纯随机数)在四个图像中是完全相同的。而我同事的情况则不是这样。

这情况很奇怪,我非常感谢你们能提供的任何帮助。

代码:

import multiprocessing as mp
import scipy as sp
import scipy.stats as spstat
import pylab

def testfunc(x0, N):
    print 'working with x0 = %s' % x0
    x = [x0]
    for i in xrange(1,N):
        x.append(spstat.norm.rvs(size = 1)) # stupid appending to make it slower
        if i % 10000 == 0:
            print 'x0 = %s, i = %s' % (x0, i)
    return sp.array(x)

def testfuncParallel(fargs):
    return testfunc(*fargs)


# Define Number of tasks.
nTasks = 4
N = 100000

if __name__ == '__main__':

    """
    Try number 1. Using multiprocessing.Pool together with Pool.map_async
    """
    pool = mp.Pool(processes = nTasks) # I have 12 threads (six cores) available so I am suprised that it does not get access to nTasks = 4 amount of workers

    # Define tasks:
    tasks = [(x, n) for x, n in enumerate(nTasks*[N])] # nTasks different tasks

    # Compute parallel: async - asynchronically, i.e. not necessary in order.
    result = pool.map_async(testfuncParallel, tasks)

    pool.close() # These are needed if map_async is used
    pool.join()

    # Get results:
    sim = sp.zeros((N, nTasks)) 

    for nn, res in enumerate(result.get()):    
        sim[:, nn] = res

    pylab.figure()
    for i in xrange(nTasks):
        pylab.subplot(nTasks,1, i + 1)
        pylab.plot(sim[:, i])

    pylab.show()

提前谢谢你们。

真诚的,
Matias

2 个回答

8

我没有你第一个问题的解决办法。实际上,我在我的64位Ubuntu系统上,使用Enthought的Python 2.7.1 [EPD 7.0-2 (64-bit)],可以毫无问题地多次运行你的代码。补充说明:问题其实是由你的IDE(Wingware)引起的。一个简单的解决办法就是在IDE外面运行这个脚本。

至于第二个问题,发生的情况是,在Unix系统上,每个工作进程都会从父进程那里继承相同的随机数生成器状态。这就是为什么它们会生成相同的伪随机序列。要解决这个问题,你只需要在testfunc的开头调用一下scipy.random.seed

def testfunc(x0, N):
    sp.random.seed()
    print 'working with x0 = %s' % x0
    ...
1

更新:结果发现这和matplotlib或其后端没有关系,而是和多进程的一些问题有关。我们已经在Wing版本4.0.4及以上修复了这个问题。解决方法是不要在子进程中执行的代码里设置断点。

看起来是Wing IDE对Tkinter后端的matplotlib支持和多进程之间有冲突。当我尝试这个例子时,它在TCL/Tk代码中崩溃。我怀疑在Windows上工作的人使用了不同的matplotlib后端。

在项目属性的扩展选项卡中关闭“matplotlib事件循环支持”似乎可以解决这个问题。

或者,当“matplotlib事件循环支持”开启时,添加以下代码似乎可以解决我的问题。

import matplotlib
matplotlib.use('WXAgg')

这只有在你有WXAgg后端的情况下才有效。Wing IDE支持的其他后端(这样即使调试过程暂停,图表仍然可以交互)有GTKAgg和Qt4Agg,但我还没有尝试过这些。

我会看看能否找到并修复这个问题。我怀疑我们需要在进程ID改变时禁用我们的事件循环支持。谢谢你报告这个问题。

撰写回答