Почему asyncio блокируется с помощью processPool? - PullRequest
0 голосов
/ 27 апреля 2018

У меня есть следующий код:

import time
import asyncio
from concurrent.futures import ProcessPoolExecutor

def blocking_func(x):
    print("In blocking waiting")
    time.sleep(x) # Pretend this is expensive calculations
    print("after blocking waiting")
    return x * 5

@asyncio.coroutine
def main():
    executor = ProcessPoolExecutor()

    out = yield from loop.run_in_executor(executor, blocking_func, 2)  # This does not
    print("after process pool")
    print(out)

if __name__ == "__main__":
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main())

Выход:

In blocking waiting
after blocking waiting
after process pool
10

Но я ожидал, что пул процессов будет запускать код в другом процессе. Поэтому я ожидал, что результат будет:

Ожидаемый результат:

In blocking waiting
after process pool
after blocking waiting
10

Я думал, что если мы запустим код в пуле процессов, он не заблокирует основной цикл. Но в результате он вернулся в основной цикл событий после того, как это было сделано с функцией блокировки.

Что блокирует цикл событий? Это функция блокировки? Если это blocking_function, какая польза от наличия пула процессов?

Ответы [ 2 ]

0 голосов
/ 27 апреля 2018

yield from здесь означает «дождаться завершения сопрограммы и вернуть ее результат». По сравнению с Python Threading API это похоже на вызов join().

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

@asyncio.coroutine
def main():
    executor = ProcessPoolExecutor()

    task = loop.run_in_executor(executor, blocking_func, 2)
    # at this point your blocking func is already running
    # in the executor process

    print("after process pool")

    out = yield from task

    print(out)
0 голосов
/ 27 апреля 2018

Сопрограммы не являются отдельными процессами. Разница в том, что сопрограммам необходимо самим отказаться от контроля над циклом. Это означает, что если у вас есть блокирующая сопрограмма, она заблокирует весь цикл.

Причина, по которой вы используете сопрограммы, главным образом для обработки операций ввода-вывода. Если вы ждете сообщения, вы можете просто проверить сокет, а если ничего не произойдет, вы вернетесь в основной цикл. Затем другие сопрограммы могут быть обработаны, прежде чем, наконец, управление вернется к функции ввода-вывода.

В вашем случае имеет смысл использовать await asyncio.sleep(x) вместо time.sleep(x). Таким образом, управление приостанавливается с blocking_func() на время сна. После этого контроль возвращается туда, и результат должен быть таким, как вы ожидали.

Больше информации: https://docs.python.org/3/library/asyncio.html

...