Завершение процесса разрушает проклятия Python
При использовании многопроцессорной обработки python и curses кажется, что завершение процесса мешает отображению curses.
Например, в следующем коде, почему завершение процесса не дает проклятиям отображать текст? (нажатие b после нажатия a)
Точнее, кажется, что не только строка "привет" больше не отображается, но и все окно проклятий.
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)
Я использую Python 3.6 под GNU/Linux.
Редактировать:
Я все еще могу воспроизвести с этой более урезанной версией, которая не вызывает sleep(). Теперь просто нажав "а" запускает ошибку.
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 ответа
Следующий код работает:
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
перед инициализацией интерфейса с помощью curses.wrapper()
, Хорошо, почему это работает? Для этого мы должны знать, как работает Proccess и что именно он делает, когда вы звоните Process(target = hang)
:
вилка
Родительский процесс использует os.fork() для разветвления интерпретатора Python. Дочерний процесс, когда он начинается, фактически идентичен родительскому процессу. Все ресурсы родителя наследуются дочерним процессом. Обратите внимание, что безопасное разветвление многопоточного процесса проблематично.
Доступно только в Unix. По умолчанию в Unix.
Теперь, что это говорит нам? Вы где создаете новый Processes
когда вы уже создали curses
экран. Что делает curses.wrapper()?
Перед вызовом func wrapper () включает режим cbreak, отключает эхо, включает клавиатуру терминала и инициализирует цвета, если терминал поддерживает цвета. При выходе (как обычно, так и по исключению) он восстанавливает режим приготовления, включает эхо и отключает клавиатуру терминала.
Хорошо, у нас есть недавно созданный дочерний процесс, который имеет те же ресурсы, что и его родительский процесс. Когда вы звоните terminate()
чтобы убить ребенка, он освобождает все ресурсы, включая обертку curses. Он восстанавливает предыдущие настройки терминала и, следовательно, нарушает ваш пользовательский интерфейс.
Чтобы это исправить, вы должны реализовать свою программу по-другому. Заранее создайте новый процесс, используйте IPC для связи с вашим процессом, используйте пулы процессов, если вам нужно несколько процессов, потоки или пулы потоков, если у вас есть задачи, связанные с вводом-выводом.
Возможно, вы используете это на Windows? Одним из документированных требований многопроцессорного модуля на этой платформе является то, что весь код верхнего уровня (curses.wrapper(display)
в вашем случае) ДОЛЖЕН быть внутри if __name__ == '__main__':
блок, чтобы он не был случайно выполнен в вашем порожденном процессе.
Я думаю, что здесь происходит то, что ваш порожденный процесс инициализирует проклятия сам (что включает в себя соответствующую настройку консоли), а затем возвращает консоль в нормальное состояние после ее завершения - тем самым отменяя настройку, выполненную исходной программой.