gpt4 book ai didi

python - Matplotlib 和多处理 RuntimeError

转载 作者:太空狗 更新时间:2023-10-30 02:32:53 27 4
gpt4 key购买 nike

我正在尝试同时使用多处理和 matplotlib。

我正在创建一个标准的 Pool,添加与 apply_async 的工作,并使用 apply_async 的回调函数更新 GUI,它运行于Pool 的父进程(我用 os.getpid() 验证了这一点)。示例:

from pylab import *
from numpy import *
from numpy.random import random
from multiprocessing import Pool

# Output image
global out_all
out_all = zeros((256, 256))

# Only does something to in_image, doesn't access anything else
def do_work(in_image):
for x in xrange(100000):
out_image = in_image[::-1, ::-1]
return out_image

# Update the output image and display if needed
def do_update(out_image):
global out_all
print ("Updating")
out_all += out_image
clf()
imshow(out_all)
show()

# Input images (close enough to what I do as well)
work = [random((256, 256)) for f in range(20)]

# Don't block when showing something
ion()

# Do the work
print "Starting pool"
pool = Pool()
for o in work:
pool.apply_async(do_work, [o], callback=do_update).get()
pool.close()
pool.join()
print "Stopping pool"

# Block
ioff()
show()
print "Done"

处理本身工作正常,进程确实在 pool.join() 上被破坏了,但是 Matplotlib(和 TK,我猜)在我之后尝试做某事时立即提示,即使只是退出程序:

Traceback (most recent call last):
File "test_thread.py", line 27, in <module>
show()
File "/usr/lib/pymodules/python2.7/matplotlib/pyplot.py", line 139, in show
_show(*args, **kw)
File "/usr/lib/pymodules/python2.7/matplotlib/backend_bases.py", line 83, in __call__
manager.show()
File "/usr/lib/pymodules/python2.7/matplotlib/backends/backend_tkagg.py", line 444, in show
self.canvas.draw_idle()
File "/usr/lib/pymodules/python2.7/matplotlib/backends/backend_tkagg.py", line 258, in draw_idle
self._idle_callback = self._tkcanvas.after_idle(idle_draw)
File "/usr/lib/python2.7/lib-tk/Tkinter.py", line 512, in after_idle
return self.after('idle', func, *args)
File "/usr/lib/python2.7/lib-tk/Tkinter.py", line 504, in after
name = self._register(callit)
File "/usr/lib/python2.7/lib-tk/Tkinter.py", line 1101, in _register
self.tk.createcommand(name, f)
RuntimeError: main thread is not in main loop
Error in atexit._run_exitfuncs:
Traceback (most recent call last):
File "/usr/lib/python2.7/atexit.py", line 24, in _run_exitfuncs
func(*targs, **kargs)
File "/usr/lib/pymodules/python2.7/matplotlib/_pylab_helpers.py", line 82, in destroy_all
manager.destroy()
File "/usr/lib/pymodules/python2.7/matplotlib/backends/backend_tkagg.py", line 452, in destroy
self.canvas._tkcanvas.after_cancel(self.canvas._idle_callback)
File "/usr/lib/python2.7/lib-tk/Tkinter.py", line 519, in after_cancel
data = self.tk.call('after', 'info', id)
RuntimeError: main thread is not in main loop
Error in sys.exitfunc:
Traceback (most recent call last):
File "/usr/lib/python2.7/atexit.py", line 24, in _run_exitfuncs
func(*targs, **kargs)
File "/usr/lib/pymodules/python2.7/matplotlib/_pylab_helpers.py", line 82, in destroy_all
manager.destroy()
File "/usr/lib/pymodules/python2.7/matplotlib/backends/backend_tkagg.py", line 452, in destroy
self.canvas._tkcanvas.after_cancel(self.canvas._idle_callback)
File "/usr/lib/python2.7/lib-tk/Tkinter.py", line 519, in after_cancel
data = self.tk.call('after', 'info', id)
RuntimeError: main thread is not in main loop

我的第一个想法是 TK 上下文在每个 fork() 上都是重复的,这以某种方式干扰了主进程中的 TK 循环,但我在我的程序中没有做任何与 TK 相关的事情 worker 。有什么想法吗?

最佳答案

错误消息引用 Tkinter .所以看起来你正在使用 TkAgg 后端。下面的代码是特定于 TkAgg/Tkinter 的。特别是电话

win.after(100, animate)

使用 Tkinter 特定的 after方法。对 GtkAgg/PyGtk 有类似的调用,对其他后端也有类似的调用。但我只想强调以下内容是 TkAgg/Tkinter 特定的。


Tkinter 旨在在单线程中运行。也就是说,所有 Tkinter GUI 调用都应源自单个线程(通常,不一定是主线程)。

游泳池的 apply_async回调方法在主进程中的单独 ( _handle_results) 线程中运行。自 imshow()从 Pool 的 _handle_results 调用线程和show()在主线程中被调用,Tkinter提示

RuntimeError: main thread is not in main loop

我看不到使用 apply_async 的方法在这种情况下回调。

相反,我们可以安排 do_workout_imagemultiprocessing.Queue() (我在下面的代码中称之为 out_queue)。然后,我们将让主进程的主线程轮询此队列以查找项,并在它们从队列中出来时显示它们。此轮询在 animate 中完成函数,如下。


plt.ion()仅适用于交互式 session 。虽然有时可以编写一些似乎可以与 plt.ion() 一起使用的小脚本。 ,如果您拒绝使用 plt.ion(),您将获得更好的结果和更清晰的 GUI在脚本中,而是编写符合 GUI 框架事件循环的代码。

尽管修复脚本并使用 plt.ion() 可能是可能的,因为这不是编写 matplotlib 脚本的推荐方式,让我们看看是否可以避免这样做。


plt.show()告诉 Tkinter 运行它的事件循环。请注意,一旦进行此调用,就会绘制 GUI 窗口,您可以单击按钮、放大和缩小等。

我们需要以某种方式向这个事件循环中注入(inject)一个函数,由事件循环定期运行,并与可能发生的所有其他 GUI 事件协作。我们希望此函数检查是否有任何工作子进程为我们提供输出,如果有,则更新 imshow 图像。

用TkAgg/Tkinter,注入(inject)这样一个函数的方式是

win = fig.canvas.manager.window
win.after(100, animate)

这将告诉 Tkinter 运行函数 animate (一次)在(大约)100 毫秒过去后。因为我们想要函数 animate要定期运行,我们只需坚持另一个

win.after(100, animate)

animate 末尾调用.


import matplotlib as mpl
mpl.use('TkAgg')
import matplotlib.pyplot as plt
import numpy as np
import multiprocessing as mp
import logging
import Queue
logger = mp.log_to_stderr(logging.INFO)

# Only does something to in_image, doesn't access anything else
def do_work(in_image):
logger.info('Processing in_image')
for x in xrange(100000):
out_image = in_image[::-1, ::-1]
out_queue.put(out_image)

# Update the output image and display if needed
out_all = np.zeros((256, 256))


def pool_initializer(out_queue_):
# Setup out_queue as a global variable *in the worker subprocesses*
global out_queue
out_queue = out_queue_


def animate():
global out_all
try:
out_image = out_queue.get_nowait()
except Queue.Empty:
pass
else:
logger.info("Updating")
out_all += out_image
im.set_data(out_all)
fig.canvas.draw() # redraw the canvas
win.after(100, animate)

if __name__ == '__main__':
out_queue = mp.Queue()
logger.info("Starting pool")
pool = mp.Pool(initializer=pool_initializer, initargs=(out_queue, ))
work = [np.random.random((256, 256)) for f in range(20)]
for o in work:
pool.apply_async(do_work, [o])
pool.close()

fig, ax = plt.subplots()
win = fig.canvas.manager.window
# Output image
im = plt.imshow(out_all, vmin=0, vmax=1)

# Register a function to be run once
win.after(100, animate)
plt.show()
logger.info("Done")

关于python - Matplotlib 和多处理 RuntimeError,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16016102/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com