Matplotlib:多线程同时绘图

31

我想尝试在并行中进行一些绘图操作,以便更快地完成大批量的工作。为此,我为每个要进行的绘图启动一个线程。

我曾希望每个线程都能完成其绘图并关闭自己(据我所知,Python会在run()语句执行完毕后关闭线程)。下面是一些展示这种行为的代码。

如果注释掉创建图形的那行代码,则程序会按预期运行。另一个可能有用的提示是当您只生成一个线程时,它也可以按预期运行。

import matplotlib.pyplot as plt
import time
import Queue
import threading

def TapHistplots():
    ##  for item in ['str1']:
# # it behaves as expected if the line above is used instead of the one below
    for item in ['str1','str2']:
        otheritem = 1
        TapHistQueue.put((item, otheritem))
        makeTapHist().start()

class makeTapHist(threading.Thread):
    def run(self):
        item, otheritem = TapHistQueue.get()
        fig = FigureQueue.get()
        FigureQueue.put(fig+1)
        print item+':'+str(fig)+'\n',
        time.sleep(1.3)
        plt.figure(fig) # comment out this line and it behaves as expected
        plt.close(fig)

TapHistQueue = Queue.Queue(0)
FigureQueue = Queue.Queue(0)
def main():
    start = time.time()
    """Code in here runs only when this module is run directly"""
    FigureQueue.put(1)
    TapHistplots()
    while threading.activeCount()>1:
        time.sleep(1)
        print 'waiting on %d threads\n' % (threading.activeCount()-1),
    print '%ds elapsed' % (time.time()-start)

if __name__ == '__main__':
    main()

非常感谢您的帮助。


3
你实际上并没有说明出现了什么问题,尽管听起来像是某种线程并发问题。 - Thomas K
我其实不确定出了什么问题。我没有收到任何错误信息,而且Python进程一直在运行。此外,主线程中的打印语句应该每秒钟都会执行一次,但第一秒后就停止了。任务管理器显示该进程继续使用大量CPU资源。很遗憾,我在严重调试方面缺乏经验。 - Boris
你是想要多次调用 makeTapHist().start() 吗?看起来它应该放在循环体外面。 - Mad Physicist
2个回答

33

为什么不直接使用多进程?从您的描述中,我认为线程对您也没有太大帮助...

Matplotlib已经使用了线程,这样您就可以同时显示和交互多个图形。如果您想在多核计算机上加速批处理,您无论如何都需要使用多进程。

以下是一个基本示例(警告:这将在您运行它的目录中创建20个小的.png文件!

import multiprocessing
import matplotlib.pyplot as plt
import numpy as np

def main():
    pool = multiprocessing.Pool()
    num_figs = 20
    input = zip(np.random.randint(10,1000,num_figs), 
                range(num_figs))
    pool.map(plot, input)

def plot(args):
    num, i = args
    fig = plt.figure()
    data = np.random.randn(num).cumsum()
    plt.plot(data)
    plt.title('Plot of a %i-element brownian noise sequence' % num)
    fig.savefig('temp_fig_%02i.png' % i)

main()

1
此外,multiprocessing 版本与 threading 版本相比速度超快。 - jfs
我刚刚在Python 3.5.3上尝试了这个脚本,但它卡住了...有什么帮助吗? - Fabio
1
在Windows上,您需要通过if __name__=='__main__': main()来保护main()调用。 - skjerns
我从Flask API调用了解决方案,它解决了并发问题。然而,创建的进程没有退出,导致了内存不足的情况。这是否正常,因为它不是生产服务器? - am.rez
1
如果您正在使用一个长期运行的服务器(例如 Flask),则还应添加这两个调用以完全关闭创建的进程:pool.close()pool.join() - am.rez

6
对于pylab接口,有一个解决方案使用线程进行异步绘图
如果没有pylab,则对于每个matplotlib后端(Qt、GTK、WX、Tk)都可能有不同的解决方案。问题在于每个GUI工具包都有自己的GUI主循环。您可以查看ipython是如何处理这个问题的。

2
就我所知,提供的链接展示了如何从多个线程中处理单个图像,而不是如何在并行情况下制作图表。据我理解,当使用matplotlib进行交互操作(比如ipython)时,后端是至关重要的。如果您能解释一下它们如何适用于此示例,我将不胜感激。 - Boris
@Boris:后端确实很重要,例如http://ideone.com/J42rn在默认后端下会产生分段错误。 - jfs
那个链接现在似乎已经失效了..? - FriendFX

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接