3

У меня есть любимый проект со следующей логикой:asyncio + многопроцессорная + Unix

import asyncio, multiprocessing 

async def sub_main(): 
    print('Hello from subprocess') 

def sub_loop(): 
    asyncio.get_event_loop().run_until_complete(sub_main()) 

def start(): 
    multiprocessing.Process(target=sub_loop).start() 

start() 

Если вы запустите ее, вы увидите:

Hello from subprocess 

Это хорошо. Но то, что я должен сделать, это сделать start() сопрограмму вместо:

async def start(): 
    multiprocessing.Process(target=sub_loop).start() 

Чтобы запустить его, я должен сделать что-то вроде этого:

asyncio.get_event_loop().run_until_complete(start()) 

Вот вопрос: когда процесс суб- создается, он получает все окружение Python клонированный, поэтому цикл событий уже работает там:

Process Process-1: 
Traceback (most recent call last): 
    File "/usr/lib/python3.5/multiprocessing/process.py", line 249, in _bootstrap 
    self.run() 
    File "/usr/lib/python3.5/multiprocessing/process.py", line 93, in run 
    self._target(*self._args, **self._kwargs) 
    File "test.py", line 7, in sub_loop 
    asyncio.get_event_loop().run_until_complete(sub_main()) 
    File "/usr/lib/python3.5/asyncio/base_events.py", line 361, in run_until_complete 
    self.run_forever() 
    File "/usr/lib/python3.5/asyncio/base_events.py", line 326, in run_forever 
    raise RuntimeError('Event loop is running.') 
RuntimeError: Event loop is running. 

Я пытался уничтожить его на подпроцесс стороне не повезло, но я думаю, что правильный путь к пр однако его совместное использование с подпроцессом. Возможно ли это как-то?

UPDATE: Вот полный код недостатка:.

import asyncio, multiprocessing 

import asyncio.unix_events 

async def sub_main(): 
    print('Hello from subprocess') 

def sub_loop(): 
    asyncio.get_event_loop().run_until_complete(sub_main()) 


async def start(): 
    multiprocessing.Process(target=sub_loop).start() 

asyncio.get_event_loop().run_until_complete(start()) 
+0

У меня нет времени для полного ответа, но вы можете рассмотреть конструкцию, где (а) ваша многопроцессорная материал выполняемый скриптом, который может быть вызван, например, с помощью. 'subprocess.Popen ([sys.executable," the_script.py "], ...)' (b) этот сценарий связывается со своим родителем, например. 'stdout', используя разработанный протокол (он может быть абсолютно простым, например, однобайтные управляющие символы для сценариев и обновлений состояния) и (c) с помощью API-интерфейса подпроцесса [asyncio] (https://docs.python.org/ 3/библиотека/asyncio-subprocess.html). – detly

+0

(я не имею в виду, что вы должны одновременно использовать подпроцесс subprocess.Popen и API-интерфейс подпроцесса asyncio, просто чтобы вы могли написать свой скрипт, чтобы он * можно было контролировать * как любой язык-агностический подпроцесс.) – detly

+0

@detly Спасибо за предложение, но есть много данных, которые должны быть унаследованы подпроцессом. Если есть простое решение, чтобы избежать упомянутой проблемы, я предпочел бы ее, а не переписывать все многопроцессорные материалы вручную. – Grief

ответ

4

Во-первых, вы должны рассмотреть возможность использования loop.run_in_executor с ProcessPoolExecutor, если вы планируете запускать подпроцессы python из цикла. Что касается вашей проблемы, вы можете использовать event loop policy функцию, чтобы установить новый цикл:

import asyncio 
from concurrent.futures import ProcessPoolExecutor 

async def sub_main(): 
    print('Hello from subprocess') 

def sub_loop(): 
    loop = asyncio.new_event_loop() 
    asyncio.set_event_loop(loop) 
    loop.run_until_complete(sub_main()) 

async def start(executor): 
    await asyncio.get_event_loop().run_in_executor(executor, sub_loop) 

if __name__ == '__main__': 
    executor = ProcessPoolExecutor() 
    asyncio.get_event_loop().run_until_complete(start(executor)) 
+0

Правильно ... Кажется, я слепой, так как я пропустил эту очевидную функцию 'asyncio.new_event_loop()'. Благодаря! Не могли бы вы объяснить мне, какая прибыль «ProcessPoolExecutor» в этом случае? – Grief

+1

@Grief 'run_in_executor' является сопрограммой, поэтому вы можете легко присоединиться к вашему подпроцессу, используя, например,' await' или 'asyncio.wait_for'. «ProcessPoolExecutor» также позволяет указать количество рабочих. – Vincent

+1

Спасибо, что указали на необходимость создания нового цикла событий в созданном подпроцессе. Это был ключевой бит, который мне не хватало - в противном случае возникла очень загадочная ошибка «Bad file descriptor». –

1

Вы всегда должны добавить проверку, чтобы увидеть, как вы работаете код (в if __name__ == '__main__': часть Вашего подпроцесс работает все в модуль 2-й раз, давая вам горе (не удержалась).

import asyncio, multiprocessing 

async def sub_main(): 
    print('Hello from subprocess') 

def sub_loop(): 
    asyncio.get_event_loop().run_until_complete(sub_main()) 


async def start(): 
    multiprocessing.Process(target=sub_loop).start() 

if __name__ == '__main__': 
    asyncio.get_event_loop().run_until_complete(start()) 
+0

Я предполагаю, что это связано только с unix, так как это заканчивается тем же результатом, я имею в виду точно такое же исключение. – Grief

+0

@Grief: Я посмотрю, смогу ли я реплицировать вашу проблему в linux env. – Gerrat

+0

Кстати, они изменили эту часть в документации python3. Это было «Для объяснения причин (в Windows) части, если __name__ == '__main__' необходимо, см.« Руководства по программированию ». Https://docs.python.org/2/library/multiprocessing.html#multiprocessing- программирование, и теперь это просто «Для объяснения причин, почему часть __name__ == '__main__» необходима, см. «Руководства по программированию». Я считаю, что ничего не изменилось под капотом здесь, и удаление Windows упоминается просто как толчок к кресту платформенное кодирование. – Grief