为何子线程中Matplotlib首次绘图仅告警可用,二次运行报错?
非主线程重复运行Matplotlib绘图的临时解决办法
问题原因
第一次运行时,Matplotlib的GUI后端(比如默认的TkAgg)在子线程里完成初始化,虽有警告但能正常工作;关闭窗口后,后端的主循环状态残留,第二次再在子线程启动绘图时,后端会尝试绑定已失效的主线程循环,直接抛出RuntimeError: main thread is not in main loop错误。
临时解决方案(两种可选)
方案1:重置Matplotlib后端+清理残留窗口
每次启动绘图线程前,强制重置Matplotlib的GUI后端并关闭所有残留窗口,让后端重新初始化。修改后的代码如下:
import numpy as np import matplotlib.pyplot as plt import matplotlib import threading, time def visualization_thread(): # 强制重置后端,确保每次线程启动都重新初始化GUI环境 matplotlib.use('TkAgg', force=True) plt.close('all') # 清理所有残留的绘图窗口 fig = plt.figure() ax = fig.add_subplot(111) l1, *_ = ax.plot(y1, color='r', label="1") fig.show() fig.canvas.flush_events() while running: l1.set_ydata(y1) fig.canvas.draw_idle() fig.canvas.flush_events() plt.pause(0.020) def data_thread(): global y1, y2, y3 while running: y1 = np.random.rand(100) time.sleep(0.020) # 第一次运行 running = True threading.Thread(target=data_thread).start() threading.Thread(target=visualization_thread).start() time.sleep(4) running = False time.sleep(2) # 第二次运行前先清理 plt.close('all') running = True threading.Thread(target=data_thread).start() threading.Thread(target=visualization_thread).start() time.sleep(4) running = False
方案2:改用多进程代替线程(更稳定)
线程共享进程内存空间,Matplotlib的后端状态会互相干扰;改用多进程的话,每个进程有独立的Matplotlib环境,完全避免线程间的状态冲突。注意进程间数据共享需要用multiprocessing的共享对象:
import numpy as np import matplotlib.pyplot as plt import multiprocessing as mp, time def visualization_thread(running, y_queue): fig = plt.figure() ax = fig.add_subplot(111) y1 = y_queue.get() l1, *_ = ax.plot(y1, color='r', label="1") fig.show() fig.canvas.flush_events() while running.value: # 从队列获取最新数据 if not y_queue.empty(): y1 = y_queue.get() l1.set_ydata(y1) fig.canvas.draw_idle() fig.canvas.flush_events() plt.pause(0.020) def data_thread(running, y_queue): while running.value: y1 = np.random.rand(100) # 将数据放入队列传递给绘图进程 if y_queue.empty(): y_queue.put(y1) time.sleep(0.020) # 第一次运行 running = mp.Value('b', True) y_queue = mp.Queue() mp.Process(target=data_thread, args=(running, y_queue)).start() mp.Process(target=visualization_thread, args=(running, y_queue)).start() time.sleep(4) running.value = False time.sleep(2) # 第二次运行 running = mp.Value('b', True) y_queue = mp.Queue() mp.Process(target=data_thread, args=(running, y_queue)).start() mp.Process(target=visualization_thread, args=(running, y_queue)).start() time.sleep(4) running.value = False
注意事项
- 方案1属于临时hack,不同Matplotlib后端可能有差异,测试时如果TkAgg不行,可以换成
Agg(无GUI)或者QtAgg试试。 - 方案2更稳定,但需要处理进程间的数据传递,上面用队列做了简单示例,实际使用可以根据需求调整。
内容的提问来源于stack exchange,提问作者Basj
相关产品推荐
相关产品推荐

