2017-06-21 95 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