使用matplotlib运行线程导致Python崩溃

3
我有一个程序,其中包含两个非常简单的线程:
  • 一个用于监听串口
  • 一个用于文本UI
我还在我的main()中运行matplotlib animation。它有一个scope类,这是从matplotlib示例中获取的。
当程序开始运行时,显示绘图,一切正常。问题是,只要用户输入键盘,程序就会崩溃,并出现致命错误导致Python退出。 ui线程与matplotlibscope类没有关系。如果删除创建绘图的代码,则ui线程没有问题,程序顺畅运行。我还注意到,我的系统上的matplotlib使用tkinter创建窗口。
您有什么提示或经验,可以解释为什么matplotlib animation会引起问题吗?线程是否不能与matplotlib plot一起使用?
我在Windows7命令行窗口中运行此程序,使用的是Python 2.7matplotlib版本:2.0.2 Tkinter版本:8.5
错误信息:
Fatal Python error: GC object already tracked

This application has requested the Runtime to terminate it in an unusual way.
Please contact the application's support team for more information.

或者是这个错误:
TclStackFree: incorrect freePtr. Call out of sequence?

This application has requested the Runtime to terminate it in an unusual way.
Please contact the application's support team for more information.

代码:

    import threading
import serial
from matplotlib.lines import Line2D
import matplotlib.pyplot as plt
import matplotlib.animation as animation

class listner(threading.Thread):

        def __init__(self,port):
            threading.Thread.__init__(self)
            self.sport=None
            self.is_running=True
            self.init_port(port)

        def run(self):
            print ' Reading from Port'
            while self.is_running:
                try:
                    self.sport.read(1)
                except:
                    print 'Error reading port'

        def init_port(self,port):
            print '1'
            if self.sport==None or not self.sport.is_open :
                try:

                    self.sport = serial.Serial(port,115200)
                    self.sport.timeout = 1
                    self.sport.reset_input_buffer()
                    self.sport.reset_output_buffer()
                    self.port_open=True
                except:
                    print "    Port error Listener Initing\n",self.port_open,'\n',self.sport
            else:
                pass

        def process(self):
            pass


class ui(threading.Thread):

        def __init__(self):
            threading.Thread.__init__(self)     
            self.running = True

        def run(self):

            print 'Starting UI:\n'
            while self.running:
                print ' Enter input ''S'':\n'
                user = raw_input()



def main(port):

        listner_thread = None
        try:
            listner_thread = listner(port)
            listner_thread.start();
        except:
            print "Listener Thread Failed To Start"
            return

        ui_thread=None
        try:
            ui_thread = ui()
            ui_thread.start()          
        except:
            print "UI Thread Failed To Start"
            return

        run_charts()



def run_charts():
        fig, (ax1, ax2) = plt.subplots(2, 1)

        scope1 = Scope(ax1)
        ani1 = animation.FuncAnimation(fig, scope1.update, emit_ch1, interval=10,blit=True)

        scope2 = Scope(ax2)
        ani2 = animation.FuncAnimation(fig, scope2.update, emit_ch2, interval=10,blit=True)

        plt.show()

def emit_ch1():
    yield 0.001

def emit_ch2():
    yield -0.001

class Scope(object):
        def __init__(self, ax, maxt=2, dt=0.02):
            self.ax = ax
            self.dt = dt
            self.maxt = maxt
            self.tdata = [0]
            self.ydata = [0]
            self.line = Line2D(self.tdata, self.ydata)
            self.ax.add_line(self.line)
            self.ax.set_ylim(-.009, 0.009)
            self.ax.set_xlim(0, self.maxt)

        def update(self, y):
            t = self.tdata[-1] + self.dt
            self.tdata.append(t)
            self.ydata.append(y)
            self.line.set_data(self.tdata, self.ydata)
            return self.line,



if __name__ == '__main__':
        main('COM11')

如果您提供一个更简单的示例,仍然存在这个问题,那么人们可能更容易调试它。请参阅 https://stackoverflow.com/help/mcve - timotree
当我在Python中放置这段代码时,我会收到以下错误: AttributeError: type object 'listner' has no attribute 'listner' 因为这行代码: listner_thread = listner.listner(port) 你确定提供的代码是在用户按下键之前可以正常工作的代码吗? - timotree
我更新了代码。现在它完全可用并且可以重现问题。 - doubleE
1个回答

2
第一个错误,“致命的Python错误:GC对象已被跟踪”在2013年被关闭,并标记为“CLOSED WONTFIX”。请参见Bugzilla上的bug报告
似乎在2015年再次引起关注(与dask相关),临时解决方案是使用以下代码只使用单个线程
   import dask
   dask.set_options(get=dask.async.get_sync)

但问题实际上在于`dataframe.read_csv`的问题。
这个问题在pandas的后续版本中最终得到了解决。如果您升级matplotlib的版本,很可能也会通过类似的修复来解决问题。
希望这可以帮助您。

谢谢提供信息。我猜Tkinter出了问题。我在另一台机器上运行了相同的代码,它使用了MPL WebAgg GUI框架,运行得很顺利。但是它会在浏览器中打开图形,这并不理想。 - doubleE
1
@dandikain 在导入pyplot之前插入这行代码'matplotlib.use('Agg')'以避免弹出窗口 - 来源 同时建议在show()之前保存您的图表,例如plt.savefig('fig')。 - Rachel Gallen
尝试过了并将其放在最开始,但它没有生效并警告我应该在调用plot!之前放置它(我确实是这样做的,在所有事情之前调用了它)。 - doubleE
根据上面的代码,应该是第四行,然后导入pyplot。(而不是把这一行放在最开始)。你试过了吗? - Rachel Gallen
是的,我做了那个。它发出以下警告: c:\Python27\lib\site-packages\matplotlib\__init__.py:1405: UserWarning: This call to matplotlib.use() has no effect because the backend has already been chosen; matplotlib.use() must be called *before* pylab, matplotlib.pyplot, or matplotlib.backends is imported for the first time. - doubleE
1
@dandikain,你能不能调试一下你的代码,找到它第一次被导入的地方(显然在这段代码之前已经被导入了)...然后在那里插入这行代码呢?除此之外,我没有其他建议了。希望你能解决这个问题。很高兴它部分地得到了解决。 - Rachel Gallen

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