Python多进程问题:持续打印数据+可重开Matplotlib图表时进程重启报错
解决多进程重启Matplotlib图表的问题
为啥会报错?
Python里的multiprocessing.Process对象是“一次性”的——只要你调用过它的start()方法,哪怕这个进程已经跑完或者被关掉了,都不能再调用start()启动它第二次,这就是你碰到AssertionError: cannot start a process twice的原因。
怎么改?每次重启都建新进程
别抱着同一个Process对象反复用,用户每次输入'y'要重启图表时,就重新创建一个新的Process实例再启动就行。另外还要注意Matplotlib在多进程里的用法:子进程里得单独初始化绘图后端,别和主进程的绘图环境搅在一起。
修正后的示例代码
import multiprocessing import time import matplotlib.pyplot as plt import numpy as np import sys import select # 模拟实时生成数据 def generate_data(): while True: yield np.random.randn() time.sleep(0.5) # 子进程负责绘图的函数 def plot_data(data_queue): # 子进程单独设置Matplotlib后端,避免和主进程冲突 plt.switch_backend('TkAgg') # 你也可以换成'Qt5Agg'之类的,看你环境支持哪个 fig, ax = plt.subplots() line, = ax.plot([], [], 'r-') ax.set_ylim(-3, 3) ax.set_title('实时数据展示') data_history = [] while True: try: # 从队列取数据,超时1秒避免卡死 data = data_queue.get(timeout=1) data_history.append(data) line.set_data(range(len(data_history)), data_history) ax.relim() ax.autoscale_view() fig.canvas.draw_idle() plt.pause(0.01) except multiprocessing.queues.Empty: # 主进程挂了就退出子进程 if not multiprocessing.parent_process().is_alive(): break except: # 捕获关闭窗口的异常,直接退出子进程 break if __name__ == '__main__': data_queue = multiprocessing.Queue(maxsize=100) data_generator = generate_data() plot_process = None print("主窗口持续打印数据,输入'y'重启图表,输入'q'退出") while True: # 生成并打印数据 data = next(data_generator) print(f"当前数据: {data:.2f}") # 把数据放进队列给绘图进程 if not data_queue.full(): data_queue.put(data) # 非阻塞处理用户输入,不打断数据打印 if select.select([sys.stdin], [], [], 0.1)[0]: user_input = sys.stdin.readline().strip().lower() if user_input == 'q': # 退出前先关掉绘图进程 if plot_process and plot_process.is_alive(): plot_process.terminate() plot_process.join() break elif user_input == 'y': # 先关掉旧的绘图进程(如果还在跑) if plot_process and plot_process.is_alive(): plot_process.terminate() plot_process.join() # 创建新的进程实例并启动 plot_process = multiprocessing.Process(target=plot_data, args=(data_queue,)) plot_process.start() print("程序退出")
几个要注意的点
- 别复用Process对象:每次重启都新建一个Process,旧的用完就处理掉,别想着二次启动。
- Matplotlib后端要隔离:子进程里单独调用
plt.switch_backend(),不然容易和主进程的绘图环境冲突,导致各种奇怪问题。 - 用队列传数据:主进程和子进程之间用
multiprocessing.Queue传数据,这是多进程里安全共享数据的方式,比直接用全局变量靠谱。 - 优雅收尾:重启或退出时,先终止旧进程并调用
join()等它彻底结束,避免留下僵尸进程。
内容的提问来源于stack exchange,提问作者Paul K.
相关产品推荐
相关产品推荐

