You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何子线程中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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 08:32:13