2017-06-21 18 views
1

pythonの多重処理とcursesを使用すると、Processを終了するとcursesの表示が妨げられます。
たとえば、次のコードでは、プロセスの終了によってcursesのテキスト表示が停止するのはなぜですか? (aを押した後にbを押すと)
もっと正確に言えば、文字列 "hello"だけでなく、cursesウィンドウ全体が表示されるようです。プロセスを終了すると、pythonの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を実行しています。

編集:
私はまだ()睡眠を呼び出すことはありません。このよりストリップダウンバージョンで再現することができますよ。今すぐ "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

、ドキュメントによれば、 '(終了用いて)プロセスがロック又はセマフォーを使用する場合'問題を引き起こす可能性があります: '関連するプロセスは、パイプまたはキューを使用している場合、この方法が使用されている場合、警告が、パイプまたはキューがやすいです他のプロセスによって使用不能になる可能性があります。同様に、プロセスがロックやセマフォーなどを取得した場合、プロセスを終了すると、他のプロセスがデッドロックする可能性があります。「スリープの仕組みがわからないが、それが原因かもしれない。 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()を使用してUIを初期化する前に新しいProcessを作成しました。さて、これはなぜ機能するのですか?

フォーク

親プロセスは、Pythonインタプリタをフォークするos.fork()を使用しています。このために我々はどのようにProccess作品と何がProcess(target = hang)を呼び出すときに、正確にそれがないと知っている必要があります。子プロセスは開始時に親プロセスと事実上同一です。親のすべてのリソースは、子プロセスによって継承されます。マルチスレッドプロセスを安全にフォークするには問題があることに注意してください。

UNIXのみで利用できます。 Unixのデフォルトです。

今、それは私たちに何を伝えていますか?すでにcursesの画面を作成したときに新しいProcessesを作成する場所です。 curses.wrapper()は何をしますか?

funcを呼び出す前に、wrapper()はcbreakモードをオンにし、エコーをオフにし、ターミナルキーパッドを有効にし、ターミナルがカラーをサポートしている場合は色を初期化します。終了時(通常または例外を問わず)、cookedモードを復元し、エコーをオンにし、ターミナルキーパッドを無効にします。

さて、親とまったく同じリソースを持つ新しく作成された子プロセスがあります。子供を殺すためにterminate()に電話すると、cursesラッパーを含むすべてのリソースが解放されます。これは以前の端末設定を復元し、UIを破壊します。

これを修正するには、プログラムを別の方法で実装する必要があります。あなたはIOバインドされたタスクを持っている場合は、複数のプロセス、ThreadingまたはThread Poolsが必要な場合は、事前に新しいプロセスを作成し、あなたのプロセスと通信するためにIPCを使用し、Process Poolsを使用しています。

+0

ありがとうございます。子プロセスのリソースを解放することが親のリソースを浪費するのはなぜなのかまだ分かりませんが? – Zil0

+0

それ以外は、私の実際のコードでは、実際にプロセスを開始するUIアクションがあります。私の理解では、プールを使用したり、あらかじめプロセスを作成しておくと、この操作の使用回数が制限されるため、使い勝手の面で問題があります。ラッパーに頼らず、手作業で初期化を受け入れることはできますか? – Zil0

+0

親のリソースを混乱させることはありませんが、curses画面は 'wrapper'を介してinitilaされているので、端末の設定をリセットします。親はこれを知らない。 – amuttsch

1

これはおそらくWindowsで実行していますか?そのプラットフォーム上のマルチプロセッシングモジュールの文書化された要件の1つは、すべてのトップレベルコード(curses.wrapper(display))がif __name__ == '__main__':ブロック内になければならないことです。これにより、生成されたプロセスで誤って実行されることはありません。

ここで起こっていることは、生成されたプロセスがcurses自体を初期化していることです(コンソールを適切に設定することを含む)、そして終了時にコンソールを通常に戻して元のプログラムによって行われた設定を元に戻します。

+0

こんにちは、お返事ありがとう、私は実際のコードを削除しながら私は忘れて、プラットフォームのinfosと必要なブロックを追加する私の質問を編集しました。しかし、それは問題に何も変わりません。あなたはそれを再現できませんか? – Zil0

+0

おそらく、 "cursesがテキストを表示しない"という意味を明確にすることもできます。プロセスを開始または停止した後に追加のテキストは表示されず、開始時に表示された「こんにちは」が継続的に更新されているため、実際に何が起こっているのかわかりません(と私は再現しようとしません瞬間)。 – jasonharper

+0

精度を追加するために編集されていますが、記述されているとは限りません。 0.5秒ごとのリフレッシュが意図されており、実際に何が起こっているかが問題です。 – Zil0

関連する問題