Спільне використання черги результатів між кількома процесами


92

Документація до multiprocessingмодуля показує, як передати чергу в процес, з якого розпочато multiprocessing.Process. Але як я можу поділитися чергою з запущеними асинхронними робочими процесами apply_async? Мені не потрібне динамічне приєднання або щось інше, просто спосіб для робітників (неодноразово) повідомляти свої результати до бази.

import multiprocessing
def worker(name, que):
    que.put("%d is done" % name)

if __name__ == '__main__':
    pool = multiprocessing.Pool(processes=3)
    q = multiprocessing.Queue()
    workers = pool.apply_async(worker, (33, q))

Це зазнає невдачі з: RuntimeError: Queue objects should only be shared between processes through inheritance. Я розумію, що це означає, і розумію пораду успадковувати, а не вимагати травлення / депікірування (і всі спеціальні обмеження для Windows). Але як же я проходжу черзі таким чином , що працює? Я не можу знайти приклад, і я спробував кілька альтернатив, які не вдалися різними способами. Допоможіть, будь ласка?

Відповіді:


133

Спробуйте використовувати multiprocessing.Manager для управління чергою, а також зробити її доступною для різних працівників.

import multiprocessing
def worker(name, que):
    que.put("%d is done" % name)

if __name__ == '__main__':
    pool = multiprocessing.Pool(processes=3)
    m = multiprocessing.Manager()
    q = m.Queue()
    workers = pool.apply_async(worker, (33, q))

Це вдалося, дякую! У моєму вихідному коді виникла непов’язана проблема із викликом асинхронізації, тому я також скопіював виправлення у вашу відповідь.
Алексіс

16
Будь-яке пояснення, чому queue.Queue()для цього не підходить?
mrgloom

@mrgloom: queue.Queueбув побудований для створення потоків, використовуючи блокування в пам'яті. У багатопроцесному середовищі кожен підпроцес отримував би власну копію queue.Queue()примірника у власному просторі пам'яті, оскільки підпроцеси не ділять пам’ять (переважно).
LeoRochael

@alexis Як отримати елементи з диспетчера (). Черга () після того, як кілька працівників вставили в нього дані?
MSS

10

multiprocessing.Poolвже має спільну чергу результатів, немає необхідності додатково залучати файл Manager.Queue. Manager.Queue- це queue.Queue(багатопотокова черга) під капотом, розташована на окремому серверному процесі та виставлена ​​через проксі. Це додає додаткові накладні витрати порівняно з внутрішньою чергою пулу. На відміну від сподівання на власну обробку результатів Пулу, результати Manager.Queueтакож не гарантовано замовляють.

Робочі процеси не запускаються .apply_async(), це вже трапляється під час створення екземпляра Pool. Що це почалося , коли ви телефонуєте pool.apply_async()нова «робота». Робочі процеси пулу запускають multiprocessing.pool.worker-функцію під капотом. Ця функція піклується про обробку нових "завдань", переданих через внутрішній пул, Pool._inqueueі про відправлення результатів батьківському по Pool._outqueue. Вказане вами funcбуде виконано протягом multiprocessing.pool.worker. funcмає лише returnщось, і результат буде автоматично відправлений батьківському.

.apply_async() негайно (асинхронно) повертає AsyncResultоб’єкт (псевдонім для ApplyResult). Вам потрібно зателефонувати .get()(блокує) цей об’єкт, щоб отримати фактичний результат. Іншим варіантом буде реєстрація функції зворотного виклику , яка запускається, як тільки результат стає готовим.

from multiprocessing import Pool

def busy_foo(i):
    """Dummy function simulating cpu-bound work."""
    for _ in range(int(10e6)):  # do stuff
        pass
    return i

if __name__ == '__main__':

    with Pool(4) as pool:
        print(pool._outqueue)  # DEMO
        results = [pool.apply_async(busy_foo, (i,)) for i in range(10)]
        # `.apply_async()` immediately returns AsyncResult (ApplyResult) object
        print(results[0])  # DEMO
        results = [res.get() for res in results]
        print(f'result: {results}')       

Приклад результату:

<multiprocessing.queues.SimpleQueue object at 0x7fa124fd67f0>
<multiprocessing.pool.ApplyResult object at 0x7fa12586da20>
result: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

Примітка: Вказівка timeout-параметру для .get()не зупинить фактичну обробку завдання в робочому середовищі, він лише розблокує очікувального батька, піднявши a multiprocessing.TimeoutError.


Цікаво, я спробую це першою можливістю. Це точно не працювало у 2012 році.
Алексіс,

Відповідно до @alexis Python 2.7 (2010) тут бракує лише менеджера контексту та параметра- error_callbackдля apply_async, тому з тих пір він не сильно змінився.
Дарконаут
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.