Я хотел бы знать, как многопроцессорная обработка выполняется правильно. Предполагая, что у меня есть список [1,2,3,4,5]
, сгенерированный функцией f1
, который записывается в Queue
(левый зеленый круг). Теперь я запускаю два процесса, вытягивающих из этой очереди (путем выполнения f2
в процессах). Они обрабатывают данные, скажем: удваивая значение и записывая его во вторую очередь. Теперь функция f3
считывает эти данные и распечатывает их.
Внутри функций есть своего рода цикл, который пытается прочитать из очереди навсегда. Как остановить этот процесс?
Идея 1
f1
отправляет не только список, но также объект None
или объект custon, class PipelineTerminator: pass
или некоторые из них, которые просто распространяются полностью вниз. f3
теперь ждет, когда None
появится, когда он там, он выйдет из цикла. Проблема: возможно, что один из двух f2
считывает и передает None
, а другой - все еще обрабатывает число. Затем последнее значение теряется.
Идея 2
f3
составляет f1
. Таким образом, функция f1
генерирует данные и трубы, порождает процессы с помощью f2
и передает все данные. После нереста и кормления он слушает вторую трубу, просто подсчитывая и обрабатывая полученные объекты. Поскольку он знает, сколько данных подано, он может завершить процессы, выполняемые f2
. Но если целью является создание конвейера обработки, различные этапы должны быть разделяемыми. Таким образом, f1
, f2
и f3
- это разные элементы конвейера, а дорогостоящие шаги выполняются параллельно.
Идея 3
Каждая часть конвейера является функцией, эта функция генерирует процессы, которые ей нравятся, и отвечает за их управление. Он знает, сколько данных и сколько данных было возвращено (возможно, с yield
). Поэтому безопасно распространять объект None
.
setup child processes
execute thread one and two and wait until both finished
thread 1:
while True:
pull from input queue
if None: break and set finished_flag
else: push to queue1 and increment counter1
thread 2:
while True:
pull from queue2
increment counter2
yield result
if counter1 == counter2 and finished_flag: break
when both threads finished: kill process pool and return.
(Вместо использования потоков, возможно, можно подумать о более разумном решении.)
Итак...
Я реализовал решение, следующее за идеей 2, подачу и ожидание результатов, но это был не конвейер с независимыми функциями, подключенными друг к другу. Он работал над задачей, которую мне пришлось управлять, но ее было сложно поддерживать.
Теперь я хотел бы услышать от вас, как вы реализуете конвейеры (легко в одном процессе с функциями генератора и т.д., но с несколькими процессами?) и обычно управляете ими.