Terminating a process breaks python curses

最后都变了- 提交于 2019-12-03 21:30:06

The following code works:

import curses
from multiprocessing import Process

p = None
def display(stdscr):
    stdscr.clear()
    curses.newwin(0,0)
    stdscr.timeout(500)
    while True:
        stdscr.addstr(1, 1, "hello")
        stdscr.refresh()
        key = stdscr.getch()
        if key == ord('a') and not p:
            p.start()
            p.terminate()

def hang():
    while True:
        temp = 1 + 1

if __name__ == '__main__':
    p = Process(target = hang)
    curses.wrapper(display)

I created a new Process before initializing the UI using curses.wrapper(). Okay, why does this work? For this we must know how Proccess works and what exactly it does when you call Process(target = hang):

fork

The parent process uses os.fork() to fork the Python interpreter. The child process, when it begins, is effectively identical to the parent process. All resources of the parent are inherited by the child process. Note that safely forking a multithreaded process is problematic.

Available on Unix only. The default on Unix.

Now, what does it tell us? You where creating a new Processes when you already created a curses screen. What does curses.wrapper() do?

Before calling func, wrapper() turns on cbreak mode, turns off echo, enables the terminal keypad, and initializes colors if the terminal has color support. On exit (whether normally or by exception) it restores cooked mode, turns on echo, and disables the terminal keypad.

Okay, we have a newly created child process that has the exact same resources as its parent. When you call terminate() to kill the child, it frees all resources, including the curses wrapper. It restores the previous terminal settings and therefore breaks your UI.

To fix this you have to implement your program differently. Create a new process beforehand, use IPC to communicate with your process, use Process Pools if you need multiple processes, Threading or Thread Pools if you have IO bound tasks.

Are you running this on Windows, perhaps? One of the documented requirements of the multiprocessing module on that platform is that all top-level code (the curses.wrapper(display) in your case) MUST be inside an if __name__ == '__main__': block, so that it isn't accidentally executed in your spawned process.

I think what's happening here is that your spawned process is initializing curses itself (which involves setting up the console appropriately), and then reverts the console to normal when it terminates - thus undoing the setup done by the original program.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!