我在看一些asyncio
包的python教程,下面是一个典型的例子:
import asyncio
async def first_function():
print("Before")
await asyncio.sleep(1)
print("After")
async def second_function():
print("Hello")
async def main():
await asyncio.gather(first_function(), second_function())
if __name__ == "__main__":
asyncio.run(main())
即使在更复杂的例子中,最里面的异步函数也是asyncio.sleep
或者来自某个“异步库”的函数。但是这些函数是如何组成非异步函数的呢?假设我有一个来自外部库的非异步函数,它的执行时间很长long_function
。(这个函数不包含任何await,例如,它可能是用另一种语言编写的)。我如何从它编写一个异步函数 Package 器(也许将来会有),以便我可以编写
import asyncio
async def long_function_wrapper():
...
#something with long_function()
...
async def first_function():
print("Before")
await long_functin_wrapper()
print("After")
async def second_function():
print("Hello")
async def main():
await asyncio.gather(waiting_function(), short_function())
if __name__ == "__main__":
asyncio.run(main())
行为与前一个示例相同?(即输出顺序相同)
2条答案
按热度按时间uxhixvfz1#
所有异步原语(
asyncio.sleep
、aiohttp.get
等)都基于通常由操作系统提供的低级并发原语,因此通常不包括其他嵌套的async
函数调用。这些方法依赖于线程、锁(互斥锁)、操作系统计时器、网络端口监听等。简而言之,当任务完成时,操作系统会通知程序。这个答案也可能是相关的。
创建异步函数的方法有很多种,最明显的一种是组合其他的异步函数,如果你想创建的异步函数是IO绑定的(网络,文件阅读,睡眠等),你可以 Package 在
asyncio.to_thread
中,它将在一个单独的线程中运行它:如果您的任务是CPU绑定的(繁重的计算),那么您将希望将其卸载到线程池(对于GIL释放任务)或进程池(对于其他任务):
epggiuax2#
如果您对
async
/await
语法背后的实际概念感兴趣,我建议您参考PEP 492。要更深入地理解这一点,您应该熟悉协程以及其背后的迭代器背后的思想。您应该理解event loop(以及更广泛的调度器)的用途,以及它如何与迭代器/生成器联系在一起。从技术上讲,解构现有的“常规”函数并将其转换为协程是可能的,但这并非易事。一般来说,由于事件循环在一个“单”线程中运行,除非您的
long_function
是异步的(即在某个点将控制权交还给调度程序),否则在async
上下文中执行它将阻塞事件循环,直到它完成为止。有一些第三方库尝试执行您所描述的操作,即“
async
-ify”非async
函数,但我无法说明这些函数在实践中的效果如何,以及它们有哪些局限性。如果你无法控制
long_function
,它是非async
的,并且你想让它和其他东西并发执行,你最好在一个单独的线程或进程中运行它(取决于它做什么),Python中这三个内置并发选项之间的区别已经在本网站和其他地方详细讨论过了。下面是一个示例:
multiprocessing vs multithreading vs asyncio