在Python中为非异步函数使用asyncio吗?

时间:2018-06-26 19:41:47

标签: python python-3.x python-asyncio

假设有一个可以进行各种数据库查询的库:

import time

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

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

我想同时执行这2个查询,而不必在方法签名中添加async或添加装饰器。这些功能完全不应该依赖于异步。

asyncio中利用这些非异步功能的最佳方法是什么?

我正在寻找某种形式的东西:

#I need an 'asyncWrapper'

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

预先感谢您的考虑和答复。

2 个答案:

答案 0 :(得分:5)

如果某些函数正在阻塞并且本质上不是异步的,则在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以使其自然异步。

答案 1 :(得分:3)

我想您是在追求并发性,并且希望不要坚持使用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的过程中被提及。