Matplotlib绘图在简单线程中冻结

4

我一直在使用Python的绘图库,最近发现了一个经过实践证明的成熟库matplotlib。然而,在创建一个简单的图表时,我遇到了一个问题。

在下面的例子中,Dummy 类的 plotme 方法在两个线程中连续运行,但第二次迭代时会卡住/冻结。 很可能是与线程本身有关的显而易见的问题,但我还没有找到它。

import matplotlib.pyplot as plt
from numpy import arange, sin, pi
import threading

class Dummy():

    def plotme(self, iteration = 1):

        print "%ix plotting... " % iteration,
        t = arange(0.0, 2.0, 0.01)
        s = sin(2*pi*t)
        plt.plot(t, s)
        plt.xlabel('time (s)')
        plt.ylabel('voltage (mV)')
        plt.title('About as simple as it gets, folks')
        #savefig("test.png") # irrelevant here
        plt.close()

    def threadme(self, iteration = 1):

        thread_plot = threading.Thread(target=self.plotme,
                                      args=(iteration,))
        thread_plot.start()
        thread_plot.join()

dummy = Dummy()
dummy.threadme(1)
dummy.threadme(2)

1
你是否知道matplotlib.pyplot中的命令不是线程安全的?你应该使用面向对象编程的方法,比如创建一个figure、一个axes对象,然后在这个axes对象上调用方法,例如ax.plot(...) - Thorsten Kranz
1个回答

5

首先,请注意pyplot接口不是线程安全的。

然后:使用“Agg”后端来创建多个非交互式图像。

以下是一个可工作的示例(由于线程问题可能存在问题):

import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
from numpy import arange, sin, pi
import threading

class Dummy():

    def plotme(self, iteration = 1):

        print "%ix plotting... " % iteration,
        t = arange(0.0, 2.0, 0.01)
        s = sin(2*pi*t)
        plt.plot(t, s)
        plt.xlabel('time (s)')
        plt.ylabel('voltage (mV)')
        plt.title('About as simple as it gets, folks')
        plt.savefig("19110942_%i_test.png" % iteration) # irrelevant here
        plt.clf()

    def threadme(self, iteration = 1):

        thread_plot = threading.Thread(target=self.plotme,
                                      args=(iteration,))
        thread_plot.start()
        thread_plot.join()

dummy = Dummy()
dummy.threadme(1)
dummy.threadme(2)

一个线程安全的版本应该是这样的:
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
from numpy import arange, sin, pi
import threading

class Dummy():

    def plotme(self, iteration = 1):

        print "%ix plotting... " % iteration,
        t = arange(0.0, 2.0, 0.01)
        s = sin(2*pi*t)

        fig, ax = plt.subplots()
        ax.plot(t, s)
        ax.set_xlabel('time (s)')
        ax.set_ylabel('voltage (mV)')
        ax.set_title('About as simple as it gets, folks (%i)' % iteration)
        fig.savefig("19110942_%i_test.png" % iteration)

    def threadme(self, iteration = 1):

        thread_plot = threading.Thread(target=self.plotme,
                                      args=(iteration,))
        thread_plot.start()
        thread_plot.join()

dummy = Dummy()
dummy.threadme(1)
dummy.threadme(2)

谢谢@Thorsten Kranz,就是这样。我应该更好地调查pyplot的“线程安全性”:) - Luis Mendes
2
注意:我在过去的几天里一直在尝试这个,可以有些自信地说,上面的第二个版本并不比第一个更“线程安全”,如果没有调用matplotlib.use('Agg'),两者都会愉快地在后续的线程运行中挂起。关键是使用非交互模式,然后两者都可以在多个线程中愉快地运行多次而不出错。 - SiHa

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