Использование asyncio для неасинхронных функций в Python? - PullRequest
0 голосов
/ 26 июня 2018

Предположим, есть библиотека, которая выполняет различные запросы к базе данных:

import time

def queryFoo():
    time.sleep(4)
    return "foo"

def queryBar():
    time.sleep(4)
    return "bar"

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

Как лучше всего использовать не асинхронные функции в asyncio?

Я ищу что-то в форме:

#I need an 'asyncWrapper'

results = asyncio.gather(asyncWrapper(queryFoo()), asyncWrapper(queryBar()))

Заранее благодарим вас за внимание и ответ.

Ответы [ 2 ]

0 голосов
/ 27 июня 2018

Если какая-то функция блокируется и не является асинхронной по своей природе, единственный правильный способ запустить ее внутри asyncio цикла событий - запустить ее внутри потока, используя run_in_executor :

# Our example blocking functions
import time


def queryFoo():
    time.sleep(3)
    return 'foo'


def queryBar():
    time.sleep(3)
    return 'bar'


# Run them using asyncio
import asyncio
from concurrent.futures import ThreadPoolExecutor


_executor = ThreadPoolExecutor(10)


async def in_thread(func):
    loop = asyncio.get_event_loop()
    return await loop.run_in_executor(_executor, func)


async def main():
    results = await asyncio.gather(
        in_thread(queryFoo), 
        in_thread(queryBar),
    )

    print(results)


if __name__ == "__main__":
    loop = asyncio.get_event_loop()
    try:
        loop.run_until_complete(main())
    finally:
        loop.run_until_complete(loop.shutdown_asyncgens())
        loop.close()

Это делает работу.

Однако, если вы хотите избежать использования потоков, единственный способ сделать это - переписать queryFoo / queryBar, чтобы он был асинхронным по своей природе.

0 голосов
/ 27 июня 2018

Я полагаю, что вы после параллелизма и, надеюсь, не настаивайте на использовании самого модуля asyncio, и в этом случае этот небольшой пример может быть полезен:

import asyncio
import time
from concurrent.futures import ThreadPoolExecutor

def queryFoo():
    time.sleep(2)
    return "FOO"

def queryBar():
    time.sleep(4)
    return "BAR"

with ThreadPoolExecutor(max_workers=2) as executor:
    foo = executor.submit(queryFoo)
    bar = executor.submit(queryBar)
    results = [foo.result(), bar.result()]

print(results)

Он запускает одновременно и queryFoo(), и queryBar() и собирает их результаты в список в том порядке, в котором они были упомянуты в задании results.

...