【问题标题】:Using asyncio for Non-async Functions in Python?在 Python 中将 asyncio 用于非异步函数?
【发布时间】:2018-06-26 19:41:47
【问题描述】:

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

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()))

提前感谢您的考虑和回复。

【问题讨论】:

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


    【解决方案1】:

    如果某些函数是阻塞的并且本质上不是异步的,那么在 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 重写为本质上是异步的。

    【讨论】:

    • 来自here,上面显示的示例仍然需要run_in_executor() 的异步函数,而我想确保将异步函数转换为同步,这样我就没有嵌套异步具有嵌套事件循环的调用。我能想到的解决方案之一是为内部异步调用生成新的事件循环并阻塞直到其结果返回,然后继续下一个外部循环。这行得通吗?
    • @EmersonXu 1) 如果你想强制同步一些异步函数,你只需要在同步函数中做loop.run_until_complete(asyncfunc())。但是一旦你这样做了,你就完全完成了异步世界:从现在开始,你所有的外部代码都是同步的。 2) 如果您想将一些同步函数转换为异步,请在其他异步函数中使用await loop.run_in_executor(_executor, syncfunc)3) 如果你想做点别的,如果没有可复现的代码示例,恐怕我很难理解。
    【解决方案2】:

    我想你是在追求并发,希望不要坚持使用 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 的作业中被提及的顺序收集到一个列表中。

    【讨论】:

    • 看起来他更新了问题,说他确实需要它成为“异步包装器”。
    猜你喜欢
    • 1970-01-01
    • 2020-03-09
    • 2022-11-16
    • 2021-12-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-12-07
    相关资源
    最近更新 更多