У меня проблема с Pool.map
в сочетании с модулем curses
от Python
. Всякий раз, когда я вычисляю большую нагрузку с Pool.map
, мой curses
пользовательский интерфейс нарушает: он больше не реагирует на экран getch
по умолчанию. Вместо того, чтобы читать в любой нажатой клавише мгновенно (и продолжая ее разбор), я могу нажать любое количество клавиш, пока не нажму кнопку enter. Иногда (в дополнение к этому) даже пользовательский интерфейс ломается (например, показывая часть моей нормальной оболочки).Python curses: проблема многопроцессорности с Pool.map?
Проклятия UI обертка
Это класс-оболочка (Screen
), который обрабатывает материал проклятий UI для меня:
# -*- coding: utf-8 -*-
import curses
class Screen(object):
def __init__(self):
# create a default screen
self.__mainscr = curses.initscr()
self.__stdscr = curses.newwin(curses.LINES - 2, curses.COLS - 2, 1, 1)
self.__max_height, self.__max_width = self.__stdscr.getmaxyx()
# start coloring
curses.start_color()
curses.use_default_colors()
# define colors
curses.init_pair(1, 197, -1) # red
curses.init_pair(2, 227, -1) # yellow
curses.init_pair(3, curses.COLOR_MAGENTA, -1)
curses.init_pair(4, curses.COLOR_GREEN, -1) # darkgreen
curses.init_pair(5, curses.COLOR_BLUE, -1)
curses.init_pair(6, curses.COLOR_BLACK, curses.COLOR_WHITE)
curses.init_pair(7, curses.COLOR_WHITE, -1)
curses.init_pair(8, curses.COLOR_CYAN, -1)
curses.init_pair(9, 209, -1) # orange
curses.init_pair(10, 47, -1) # green
def add_str_to_scr(self, add_str: str, colorpair: int = 7):
self.__stdscr.addstr(str(add_str), curses.color_pair(colorpair))
def linebreak(self):
self.__stdscr.addstr("\n")
def clear_screen(self):
self.__stdscr.clear()
def refresh_screen(self):
self.__stdscr.refresh()
def wait_for_enter_or_esc(self):
curses.noecho()
while True:
c = self.__stdscr.getch()
if c == 10 or c == 27: # 10: Enter, 27: ESC
break
curses.echo()
def get_user_input_chr(self) -> str:
return chr(self.__stdscr.getch())
def get_user_input_str(self) -> str:
return self.__stdscr.getstr().decode(encoding="utf-8")
Фактическая программа
Я написал небольшой пример, так как упомянутый отказ всегда происходит, когда я совмещаю Pool.map
в пользовательском интерфейсе curses
и имею большую рабочую нагрузку. Код просто вычисляет некоторые бесполезные элементы mult
и add
на массиве numpy
.
import curses
from screen import Screen
from multiprocessing import Pool, cpu_count
import numpy as np
s = Screen() # initializing my Screen wrapper
np.random.seed(1234) # setting the rng fixed to make results comparable
# worker function to simulate workload
def worker(arr):
return arr * 2 + 1
s.clear_screen() # cleans the screen
s.refresh_screen() # displays current buffer's content
s.add_str_to_scr("Start processing data...")
s.linebreak()
s.linebreak()
s.refresh_screen()
# data to feed worker function with (sliced by rows)
data_arr = np.random.rand(8, int(1e7)) # <-- big array for high workload
with Pool(cpu_count()) as p:
buffer = p.map(worker, [data_arr[row] for row in np.ndindex(data_arr.shape[0])])
s.add_str_to_scr("...finished processing:")
s.linebreak()
s.linebreak()
s.refresh_screen()
for row in buffer:
s.add_str_to_scr(row[0:3])
s.linebreak()
s.refresh_screen()
# *Here* the program should wait until the user presses *any* key
# and continue INSTANTLY when any key gets pressed.
# However, for big workloads, it does not react to single key presses,
# but wait for any amount of keys pressed until you hit 'Enter'
s.get_user_input_chr()
curses.endwin()
Теперь, когда я выполняю код с высокой нагрузкой (т.е. хруст массив формы (8, int(1e7)
составляет 8 строк с 10000000 столбцов) curse
«s getch
перерывы, и я получаю такое поведение:
Как вы можете видеть, я могу ударить q
(или любой другой ключ) так часто, как я хочу, но curse
«s getch
не реагирует. Я должен нажать клавишу Enter, чтобы распознать вход.
Кроме того, по какой-то причине первая строка получает замену исходной оболочки.
Такое поведение наблюдается, когда расчет Pool.map
примерно равен 1 секунде или более.
Когда я установил data_arr
в небольшой массив как np.random.rand(8, 100)
все работает как шарм, но как только я кормлю большие массивы, где вычисление происходит, как> = 1second, появляется эта странная ошибка и ломает мой curses
UI.
Любые идеи?
Является ли Pool.map
неправильным присоединением к рабочим процессам?
Вы пробовали интерфейсы более высокого уровня для проклятий, например. благословения? https://pypi.python.org/pypi/blessings/ В любом случае этого плохого поведения там нет. –
Спасибо за подсказку, я попробую. Тем не менее, мне действительно интересно, почему возникает эта странная проблема ... имеет проклятие какой-то внутренний цикл, который не может слишком долго ждать, пока другие процессы не закончатся? Мне кажется странным. – daniel451
* благословения * имеет некоторый диапазон использования (и это не «более высокий уровень» и т. Д.), Но не решает этот вопрос. –