终止进程会破坏python curses

时间:2017-06-21 13:37:50

标签: python python-multiprocessing python-curses

使用python多处理和curses,似乎终止进程会干扰curses显示 例如,在以下代码中,为什么终止进程会阻止curses显示文本? (按a后按b) 更确切地说,似乎不仅是字符串"你好"不再显示,而是整个curses窗口。

import curses
from multiprocessing import Process
from time import sleep

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

def hang():
    sleep(100)

if __name__ == '__main__':
    curses.wrapper(display)

我在GNU / Linux下运行python 3.6。

编辑:
我仍然可以使用这个没有调用sleep()的更简化的版本来重现。现在只需按下" a"触发了这个错误。

import curses
from multiprocessing import Process

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

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

if __name__ == '__main__':
    curses.wrapper(display)

2 个答案:

答案 0 :(得分:1)

你是在Windows上运行吗?该平台上多处理模块的一个记录要求是,所有顶级代码(在您的情况下为curses.wrapper(display))必须位于if __name__ == '__main__':块内,因此不会意外在你的衍生过程中执行。

我认为这里发生的事情是你的衍生进程正在初始化curses本身(这涉及适当地设置控制台),然后在终止时将控制台恢复正常 - 从而撤消原始设置所做的设置程序

答案 1 :(得分:1)

以下代码有效:

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)

在使用Process初始化UI之前,我创建了一个新的curses.wrapper()。好的,为什么这个有效?为此,我们必须知道Proccess的工作原理以及拨打Process(target = hang)时的确切行为:

  

     

父进程使用os.fork()来分叉Python解释器。子进程在开始时实际上与父进程相同。父进程的所有资源都由子进程继承。请注意,安全分叉多线程进程存在问题。

     

仅适用于Unix。 Unix上的默认值。

现在,它告诉我们什么?您在创建Processes屏幕时创建新curses的位置。 curses.wrapper()做了什么?

  

在调用func之前,wrapper()打开cbreak模式,关闭echo,启用终端键盘,并在终端有颜色支持时初始化颜色。退出时(无论是正常还是异常),它将恢复烹饪模式,打开回声,并禁用终端键盘。

好的,我们有一个新创建的子进程,它具有与其父进程完全相同的资源。当你调用terminate()来杀死孩子时,它会释放所有资源,包括curses包装器。它会恢复以前的终端设置,因此会破坏您的用户界面。

要解决此问题,您必须以不同方式实施您的程序。事先创建一个新流程,使用IPC与流程进行通信,如果需要多个流程,请使用Process Pools,如果您有IO绑定任务,请使用ThreadingThread Pools