Задачи из asyncio.gather не работают одновременно - PullRequest
0 голосов
/ 07 мая 2018

Я хочу очистить данные с веб-сайта одновременно, но обнаружил, что следующая программа НЕ выполняется одновременно.

async def return_soup(url):
    r = requests.get(url)
    r.encoding = "utf-8"
    soup = BeautifulSoup(r.text, "html.parser")

    future = asyncio.Future()
    future.set_result(soup)
    return future

async def parseURL_async(url):    
    print("Started to download {0}".format(url))
    soup = await return_soup(url)
    print("Finished downloading {0}".format(url))

    return soup

loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
t = [parseURL_async(url_1), parseURL_async(url_2)]
loop.run_until_complete(asyncio.gather(*t))

Однако эта программа начинает загружать второй контент только после его завершения. Если мое понимание верно, ключевое слово await в await return_soup(url) ожидает завершения функции и, ожидая завершения, возвращает элемент управления в цикл обработки событий, что позволяет циклу начать вторую загрузку ,

И как только функция окончательно завершает выполнение, будущий экземпляр в ней получает значение результата.

Но почему это не работает одновременно? Что мне здесь не хватает?

Ответы [ 2 ]

0 голосов
/ 07 мая 2018

Я добавлю еще немного к ответу пользователя 4815162342. Фреймворк asyncio использует сопрограммы, которые должны передавать управление потоком во время длительной операции Смотрите диаграмму в конце этого раздела для хорошего графического представления. Как упомянул пользователь 4815162342, библиотека запросов не поддерживает asyncio. Я знаю два способа сделать это одновременно. Во-первых, это сделать то, что предложил пользователь 4815162342, и переключиться на библиотеку с собственной поддержкой асинхронных запросов. Второй - запустить этот синхронный код в отдельных потоках или процессах. Последнее легко благодаря функции run_in_executor.

loop = asyncio.get_event_loop()

async def return_soup(url):
    r = await loop.run_in_executor(None, requests.get, url)
    r.encoding = "utf-8"
    return BeautifulSoup(r.text, "html.parser")

async def parseURL_async(url):    
    print("Started to download {0}".format(url))
    soup = await return_soup(url)
    print("Finished downloading {0}".format(url))

    return soup

t = [parseURL_async(url_1), parseURL_async(url_2)]
loop.run_until_complete(asyncio.gather(*t))

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

0 голосов
/ 07 мая 2018

Использование asyncio отличается от использования потоков тем, что вы не можете добавить его в существующую кодовую базу, чтобы сделать его параллельным. В частности, код, который выполняется в цикле событий asyncio , не должен блокировать - все блокирующие вызовы должны быть заменены неблокирующими версиями, которые предоставляют управление циклу событий. В вашем случае requests.get блокирует и уничтожает параллелизм, реализованный asyncio.

Чтобы избежать этой проблемы, вам нужно использовать библиотеку http, написанную с учетом asyncio, например aiohttp.

...