2017-06-21 54 views
1

使用python多进程和curses,似乎终止一个进程干扰curses显示。
例如,在下面的代码中,为什么终止进程会阻止curses显示文本? (按a后按b)
更确切地说,不仅显示字符串“hello”,而且显示整个curses窗口。终止一个进程打破python诅咒

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) 
+0

根据文档,当进程使用锁或信号量时,使用terminate()会导致问题:警告如果在关联进程使用管道或队列时使用此方法,则管道或队列负责被破坏,并可能被其他程序无法使用。同样,如果进程已经获得了锁或信号量等,那么终止它可能会导致其他进程死锁。“我不确定'睡眠'是如何实现的,但这可能是原因。 https://docs.python.org/3/library/multiprocessing.html#multiprocessing.Process.terminate – amuttsch

+0

@amuttsch好主意,但那不是,请参阅我的编辑。 – Zil0

回答

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) 

我初始化使用curses.wrapper()用户界面之前创建一个新的Process。好吧,为什么这个工作?为此,我们必须知道如何Proccess工作和究竟是什么,当你调用Process(target = hang)它:

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

仅在Unix上提供。 Unix上的默认值。

现在,它告诉我们什么?当您创建curses屏幕时,您可以在其中创建新的Processescurses.wrapper()做什么?

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

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

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

+0

谢谢。虽然我仍然不确定为什么释放孩子的资源会搅乱父母的资源? – Zil0

+0

除此之外,在我的实际代码中,我确实有一个启动进程的UI操作。据我的理解,事先使用池或创建进程会限制此操作的使用次数,这在可用性方面存在问题。不会依赖包装并手工完成初始化是一个可接受的解决方案? – Zil0

+0

它不会弄乱父项的资源,但由于curses屏幕是通过'wrapper'初始化的,它会重置终端设置。父母不知道这一点。 – amuttsch

1

你在Windows上运行这个,也许?该平台上的多处理模块的一个文档要求是所有顶级代码(您的案例中的curses.wrapper(display))必须位于if __name__ == '__main__':块内,以便它不会在产生的进程中意外执行。

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

+0

嗨,感谢您的回答,我编辑了我的问题,添加了平台信息和所需的块,我在剥离实际代码时忘记了它。尽管如此,它并没有改变这个问题。你无法重现吗? – Zil0

+0

也许你也可以澄清你的意思,“停止显示文本的诅咒”。开始或停止过程后,不会显示其他文本,并且在开始时显示的“hello”正在不断刷新,因此我不确定实际发生了什么(并且我无法尝试在此时此刻)。 – jasonharper

+0

编辑添加精度,但应该可以看到而不是描述。每0.5秒刷新一次,实际发生的是这个问题。 – Zil0