Matplotlib 为什么不能在另一个线程中绘制图形

Matplotlib 为什么不能在另一个线程中绘制图形

Matplotlib 是一个流行的 Python 可视化库,用于绘制统计图、热图、轮廓图等各种类型的图表。然而,许多人在使用 Matplotlib 时都会遇到一个问题:为什么 Matplotlib 不能在另一个线程中绘制图形?

阅读更多:Matplotlib 教程

原因

要理解 Matplotlib 不能在另一个线程中绘图的原因,需要了解 Matplotlib 的工作原理。

Matplotlib 是不支持并行绘图的,每个图形实例都必须在主线程中创建并绘制。这是由于 Matplotlib 使用了基于全局状态的模型来管理子图、坐标轴和图形。多个线程同时绘图会引起这些全局状态之间的竞态条件,导致程序崩溃或图像渲染异常。

举个例子,假设我们有两个线程 A 和 B ,分别在不同的时间点调用 Matplotlib 的函数来创建和显示图形:

import threading
import matplotlib.pyplot as plt

def plot():
    x = [1, 2, 3]
    y = [4, 5, 6]
    plt.plot(x, y)
    plt.show()

t1 = threading.Thread(target=plot)
t2 = threading.Thread(target=plot)
t1.start()
t2.start()

这个程序会引起两个绘图线程同时访问 Matplotlib 的全局状态,从而导致竞态条件发生。由于 Matplotlib 在本质上是单线程应用程序,因此不能解决多线程问题,就会出现各种诡异的情况。

解决方法

虽然 Matplotlib 不能并行绘图,但是可以在多个线程中分别创建和显示图形。这意味着每个线程需要拥有自己的图形实例。可以通过以下两种方式来实现:

1. 使用进程池

可以使用 Python 标准库中的 multiprocessing 模块创建一个进程池,每个进程都可以在不同的线程中运行,从而避免线程间的竞争条件。

from multiprocessing import Pool
import matplotlib.pyplot as plt

def plot_thread(x, y):
    plt.plot(x, y)
    plt.show()

if __name__ == '__main__':
    data = [(1, 4), (2, 5), (3, 6)]
    with Pool(2) as p:
        p.map(lambda x: plot_thread(*x), data)

这个程序使用了进程池来将绘图任务分配给两个进程,并行绘制图形。

2. 使用一个像Tkinter这样支持异步的GUI库

可以使用一个类似于 Tkinter 的 GUI 库,这类库支持在异步模式下绘图,可以在另一个线程中绘制图形。

import threading
import matplotlib.pyplot as plt
import tkinter as tk

class PlotApp(tk.Tk):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        plt.ion()
        self.plotting_surface = plt.figure(figsize=(5, 4), dpi=100).canvas
        threading.Thread(target=self.update_plot).start()

    def update_plot(self):
        while True:
            self.plotting_surface.draw()
            self.plotting_surface.flush_events()

if __name__ == '__main__':
    app = PlotApp()
    app.mainloop()

这个程序使用了 Tkinter 来创建一个 GUI 窗口,并在另一个线程中更新图形。在更新过程中,程序自动调用 plt.pause() 函数,使图形在不同的时间点渲染。这与主线程中绘图是同步更新的方式不同。

总结

Matplotlib 不能在另一个线程中绘图,因为使用了基于全局状态的模型来管理子图、坐标轴和图形。这导致多个线程同时访问全局状态,引发竞态条件。解决这个问题的方法是,在不同的线程中分别创建和显示图形,或者使用一个支持异步更新的 GUI 库。这些方法都可以避免线程之间的竞争条件,实现并行绘图。

Python教程

Java教程

Web教程

数据库教程

图形图像教程

大数据教程

开发工具教程

计算机教程