简体   繁体   English

多处理和 matlibplot,非阻塞图

[英]Multiprocessing and matlibplot, non-blocking plots

I know there are quite a few other questions on this topic and i have read/experimented with the solutions but none of them quite do what i want or dont work ...我知道关于这个主题还有很多其他问题,我已经阅读/试验了这些解决方案,但没有一个完全符合我的要求或不工作......

What im trying to do:我想做什么:

I would like to create a plot using matplotlib with the plot launched in a new process such that it is open and responsive while the main thread is running, additionally when the main thread exits not to kill the child processes.我想使用 matplotlib 创建一个绘图,该绘图在一个新进程中启动,以便在主线程运行时它是开放的并具有响应性,另外当主线程退出时不会杀死子进程。 I would like something portable if possible hence i have avoided fork so far as my impression is that it isn't (correct?).如果可能的话,我想要一些便携的东西,因此我一直避免使用 fork,因为我的印象是它不是(正确的?)。 I have tried the show(block=False) in matplotlib.我在 matplotlib 中尝试过 show(block=False)。 If there isn't anything else im happy to accept that but preferably the main thread exits and the plots are still sitting there.如果没有其他任何东西,我很乐意接受,但最好是主线程退出并且情节仍在那里。

My best attempt so far:到目前为止我最好的尝试:

This code is modified from Python close children when closing main process此代码是在关闭主进程时Python close children修改的

import logging, signal, sys, time
import multiprocessing as mp 
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid.axislines import SubplotZero

class AddProcessNameFilter(logging.Filter):
    """Add missing on Python 2.4 `record.processName` attribute."""
    def filter(self, r):
        r.processName = getattr(r, 'processName', mp.current_process().name)
        return logging.Filter.filter(self, r)

def print_dot(plt):
    signal.signal(signal.SIGTERM, signal.SIG_IGN)
    while True:
        mp.get_logger().info(".")
        plt.show(block=True)

def main():
    logger = mp.log_to_stderr()
    logger.setLevel(logging.INFO)
    logger.addFilter(AddProcessNameFilter()) # fix logging records

    fig = plt.figure()
    ax = fig.add_subplot(111)
    ax.plot([0, 1, 2, 3], [1, 1, 3, 2],'ro')

    # create daemonic child processes
    p = mp.Process(target=print_dot, args=(plt,))
    p.daemon = True
    p.start()    

if __name__=="__main__":
    mp.freeze_support()
    main()

What happens to this code ...这段代码会发生什么......

The plot isn't launched additionally the main thread doesn't exit it just waits on join.情节不会另外启动,主线程不会退出它只是等待加入。 When interrupted I get the following error as well ...中断时,我也会收到以下错误...

X Error of failed request:  BadIDChoice (invalid resource ID chosen for this connection)
  Major opcode of failed request:  53 (X_CreatePixmap)
  Resource id in failed request:  0x4400017
  Serial number of failed request:  344
  Current serial number in output stream:  352

Not really sure what to make of that.不太确定该怎么做。 Any ideas?有任何想法吗? Thanks in advance :D提前致谢

PS: Ubuntu 12.04 ... but i don't know if thats particularly relevant in this case ... PS:Ubuntu 12.04 ......但我不知道这在这种情况下是否特别相关......

I use the following code.我使用以下代码。 It's similar to yours, but joins old processes as the function is supposed to be called many times.它与您的类似,但加入了旧进程,因为该函数应该被多次调用。 You can close the main process and the plot won't be closed.你可以关闭主进程,情节不会被关闭。

There's no guarantee that the plot will be shown just after the p.start is called.不能保证在调用 p.start 后会立即显示该图。 But this would be very unusual and happened to me just few times.但这将是非常不寻常的,并且只发生在我身上几次。 And in these times the window is shown either if plot is called again or if I call p.is_alive().在这些时候,如果再次调用 plot 或者我调用 p.is_alive(),则会显示窗口。

    import multiprocessing as mp
    import sys
    def foo(*arg):
        import matplotlib.pyplot as plt
        plt.ioff()    
        plt.plot(*arg)
        plt.show()

    if __name__ == '__main__':
        mp.freeze_support()
        arg=(range(10),)
        global pl

        #create the process list, if it does not exists
        try:
            pl
        except NameError:
            pl = []    

        #Join old ones to keep zombie process list small
        for i in range(len(pl)):
            if not (pl[i] is None):
                if not pl[i].is_alive():
                    pl.pop(i).join()


        #create plot process
        p = mp.Process(target=foo, args=arg)
        p.start()
        pl.append(p)

I recommend a solution from https://stackoverflow.com/a/62354049/13995577 .我推荐来自https://stackoverflow.com/a/62354049/13995577的解决方案。 Works well for me.对我来说效果很好。 It is a bit of a hack though, but relatively small.虽然它有点黑客,但相对较小。

import multiprocessing as mp
def my_plot_routine(q):
    ...
    q.put('Done, see plot window')
    plt.show()
    return

class EverLastingProcess(mp.Process):
    def join(self, *args, **kwargs):
        pass

    def __del__(self):
        pass

if __name__ == '__main__':
    # create plot process
    mp.set_start_method('spawn')
    q = mp.Queue()
    p = EverLastingProcess(target=my_plot_routine, args=(q,))
    p.start()
    print(q.get())
 

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM