Matplotlib运行多进程时plt.pause报错、无绘图输出问题
Matplotlib多线程运行正常、多进程模式失效问题说明
问题复现
最初多线程实现
最初使用Python threading 多线程实现Matplotlib实时绘图的代码如下:
class Viewer: def __init__(self): _, self.ax = plt.subplots(1, 1) self.ax.set_ylim((-self.ax_lim, self.ax_lim)) self.ax.set_xlim((-self.ax_lim, self.ax_lim)) self.ax.set_aspect('equal') self.ax.grid() self.textvar = self.ax.text(-self.ax_lim-10, -self.ax_lim-10, '0.0') self.i=0 # 多线程部分 x = threading.Thread(target=self.thread_function, args=(1,)) x.start() plt.show() def thread_function(self,name): while(self.i<5000): # 执行绘图操作 self.i+=1 #plt.pause(0.01) # 省略具体绘图逻辑
运行该脚本可正常弹出Matplotlib绘图窗口,内容支持实时刷新,但受Python GIL机制限制,多线程模式运行速度极慢。
多进程改造后的异常
参考多线程性能瓶颈的相关说明,改用multiprocessing多进程实现,代码如下:
from multiprocessing import Process class Viewer: def __init__(self): _, self.ax = plt.subplots(1, 1) self.ax.set_ylim((-self.ax_lim, self.ax_lim)) self.ax.set_xlim((-self.ax_lim, self.ax_lim)) self.ax.set_aspect('equal') self.ax.grid() self.textvar = self.ax.text(-self.ax_lim-10, -self.ax_lim-10, '0.0') self.i=0 # 多进程部分 x=Process(target=self.thread_function,args=(1,)) x.start() plt.show() def thread_function(self,name): while(self.i<5000): # 执行绘图操作 self.i+=1 #plt.pause(0.01) ##<--- 此处调用会触发错误 time.sleep(0.01) # 省略具体绘图逻辑
改造后运行出现两类异常:
- 子进程调用
plt.pause时触发XCB相关X服务报错,程序直接崩溃,错误信息如下:
[xcb] Unknown sequence number while processing queue [xcb] Most likely this is a multi-threaded client and XInitThreads has not been called [xcb] Aborting, sorry about that. python: ../../src/xcb_io.c:259: poll_for_event: Assertion `!xcb_xlib_threads_sequence_lost' failed. XIO: fatal IO error 11 (Resource temporarily unavailable) on X server ":1" after 348 requests (348 known processed) with 0 events remaining. Aborted (core dumped)
- 将
plt.pause替换为time.sleep(0.01)后无报错,但Matplotlib窗口无任何绘图内容渲染输出。
根本原因
两种模式表现差异的核心逻辑如下:
- 多线程模式可正常运行的原因:多线程属于同一进程,所有线程共享进程内存空间,Matplotlib创建的画布、坐标轴等对象在所有线程中指向同一块内存地址。
plt.show()启动的GUI主事件循环可以正常读取到子线程对绘图对象的修改,因此可以正常刷新渲染,仅因为GIL的存在,CPU密集型任务无法并行,导致运行速度慢。 - 多进程模式无渲染输出的原因:多进程间内存是完全隔离的,
Process启动子进程时,子进程拿到的是主进程所有对象的拷贝副本,而非原对象。子进程中对self.ax、self.i等属性的修改,全部作用在子进程独立内存的副本上,主进程中运行GUI事件循环的Matplotlib实例收不到任何修改信号,自然不会有内容更新。 - 多进程模式XCB报错的原因:Linux下X11 GUI服务的连接与创建它的进程绑定,子进程中调用
plt.pause时,子进程内的Matplotlib副本会尝试直接和X Server通信,跨进程发起GUI请求未做线程安全初始化(即报错中提到的未调用XInitThreads),会直接触发XCB事件队列校验失败,导致程序崩溃。
正确实现方案
- 所有Matplotlib绘图、刷新操作必须放在运行GUI事件循环的主进程中执行,禁止子进程直接操作Matplotlib对象
- 多进程仅负责运行计算密集型的业务逻辑,计算完成的结果通过进程间通信方式(如
multiprocessing.Queue、Pipe)传回主进程 - 主进程可通过Matplotlib自带的定时器(
canvas.new_timer)定期从通信队列读取新数据,更新绘图元素后触发画布刷新
内容的提问来源于stack exchange,提问作者KansaiRobot
相关产品推荐
相关产品推荐

