Разделяя многие очередях среди процессов в Python

я в курсе multiprocessing.Manager() и как он может использоваться для создания общих объектов, в частности очередей, которые могут быть разделены между работниками. Есть этот вопрос, этот вопрос, этот вопрос и даже один из моих собственных вопросов.

однако мне нужно определить множество очередей, каждая из которых связывает определенную пару процессов. Скажем, что каждая пара процессов и ее связывающая очередь идентифицируются переменная key.

я хочу использовать словарь для доступа к моим очередям, когда мне нужно поместить и получить данные. Я не могу заставить это работать. Я много чего перепробовал. С multiprocessing импортировать как mp:

определение dict как for key in all_keys: DICT[key] = mp.Queue в конфигурационном файле, который импортируется в модуль многопроцессорной обработки (назовем его multi.py) не возвращает ошибок, но очередь DICT[key] не разделяется между процессами, каждый из них, похоже, имеет свою собственную копию очереди и, следовательно, нет общение происходит.

если я попытаюсь определить DICT в начале основной многопроцессорной функции, которая определяет процессы и запускает их, как

DICT = mp.Manager().dict()    
for key in all_keys:
    DICT[key] = mp.Queue()

я получаю ошибку

RuntimeError: Queue objects should only be shared between processes through
 inheritance

изменение

DICT = mp.Manager().dict()    
for key in all_keys:
    DICT[key] = mp.Manager().Queue()

только делает все хуже. Попробуйте похожие определения во главе multi.py, а не внутри функции main возвращает подобные ошибки.

должен быть способ разделить много очередей между процессами без явного именования каждого из них в коде. Есть идеи?

редактировать

вот основная схема программы:

1 - загрузить первый модуль, который определяет некоторые переменные, импорта multi запускает multi.main(), и загружает другой модуль, который запускает каскад нагрузок модулей и выполнение кода. Между тем...

2- multi.main выглядит так:

def main():
    manager = mp.Manager()
    pool = mp.Pool()
    DICT2 = manager.dict()

    for key in all_keys:
        DICT2[key] = manager.Queue()
        proc_1 = pool.apply_async(targ1,(DICT1[key],) ) #DICT1 is defined in the config file
        proc_2 =  pool.apply_async(targ2,(DICT2[key], otherargs,) 

вместо того, чтобы использовать pool и manager, я также запускал процессы со следующим:

mp.Process(target=targ1, args=(DICT[key],))

3 - функция targ1 принимает входные данные, которые поступают (сортируется по key) от основного процесса. Он предназначен для передачи результата в DICT[key] так targ2 может делать свою работу. Это часть, которая не работает. Существует произвольное число targ1s,targ2s, etc. и поэтому произвольное количество очередей.

4-Результаты некоторых из этих процессов будут отправлены в куча различных массивов / панд кадров данных, которые также индексируются key, и я хотел бы быть доступным из произвольных процессов, даже запущенных в другом модуле. Я не писал эту часть, и это может быть другой вопрос. (Я упоминаю об этом здесь, потому что ответ на 3 выше также может решить 4 красиво.)

1 ответов


похоже, ваши проблемы начались, когда вы пытались поделиться multiprocessing.Queue() передавая его как аргумент. Вы можете обойти это, создав управлял очередь вместо:

import multiprocessing
manager = mutiprocessing.Manager()
passable_queue = manager.Queue()

когда вы используете менеджер для его создания, вы храните и передаете вокруг прокси в очередь, а не в саму очередь, поэтому, даже если объект, который вы передаете своим рабочим процессам, скопирован, он все равно будет указывать на ту же базовую структуру данных: вашу очередь. Это очень похоже (по концепции) на указатели в C/C++. Если вы создаете свой очереди таким образом, Вы сможете передать их при запуске рабочего процесса.

поскольку теперь вы можете передавать очереди, вам больше не нужно управлять своим словарем. Держите обычный словарь в main, который будет хранить все сопоставления, и только дайте вашим рабочим процессам очереди, которые им нужны, поэтому им не понадобится доступ к любым сопоставлениям.

Я написал пример этого здесь. Похоже вы передаете объекты между вашими работниками, так что это то, что здесь сделано. Представьте, что у нас есть два этапа обработки, и данные как начинаются, так и заканчиваются в элементе управления main. Посмотрите, как мы можем создать очереди, которые соединяют рабочих, как трубопровод, но давая им только им очереди нужны, им не нужно знать о каких-либо отображениях:

import multiprocessing as mp

def stage1(q_in, q_out):

    q_out.put(q_in.get()+"Stage 1 did some work.\n")
    return

def stage2(q_in, q_out):

    q_out.put(q_in.get()+"Stage 2 did some work.\n")
    return

def main():

    pool = mp.Pool()
    manager = mp.Manager()

    # create managed queues
    q_main_to_s1 = manager.Queue()
    q_s1_to_s2 = manager.Queue()
    q_s2_to_main = manager.Queue()

    # launch workers, passing them the queues they need
    results_s1 = pool.apply_async(stage1, (q_main_to_s1, q_s1_to_s2))
    results_s2 = pool.apply_async(stage2, (q_s1_to_s2, q_s2_to_main))

    # Send a message into the pipeline
    q_main_to_s1.put("Main started the job.\n")

    # Wait for work to complete
    print(q_s2_to_main.get()+"Main finished the job.")

    pool.close()
    pool.join()

    return

if __name__ == "__main__":
    main()

код производит этот выход:

Main начал работу.
Этап 1 проделал некоторую работу.
Этап 2 проделал некоторую работу.
Главный закончил работу.

Я не включил пример хранения очередей или AsyncResults объекты в словарях, потому что я все еще не совсем понимаю, как ваша программа должна работать. Но теперь, когда вы можете свободно передавать свои очереди, вы можете создать свой словарь для хранения сопоставлений очереди/процесса по мере необходимости.

на самом деле, если вы действительно строите трубопровод между несколькими работниками, вам даже не нужно хранить ссылку на" рабочие " очереди в main. Создайте очереди, передайте их своим работникам, а затем сохраните ссылки только на очереди, которые main будет использовать. Я бы определенно рекомендовал попытаться позволить старым очередям собирать мусор как можно быстрее, если у вас действительно есть "произвольное количество" очередей.