是一个类似的函数:
async def f(x):
time.sleep(x)
await f(5)
适当的异步/非阻塞?
asyncio 提供的 sleep 功能有什么不同吗?
最后,aiorequests 是一个可行的异步请求替代品吗?
(在我看来,它基本上将主要组件包装为异步)
https://github.com/pohmelie/aiorequests/blob/master/aiorequests.py
最佳答案
提供的函数不是正确编写的异步函数,因为它调用了阻塞调用,这在 asyncio 中是被禁止的。 (“协程”有问题的一个快速提示是它不包含单个 await
。)禁止它的原因是阻塞调用,例如 sleep()
将暂停当前线程,而不给其他协程运行的机会。换句话说,它不会暂停当前协程,而是暂停整个事件循环,即 全部 协程。
在 asyncio(和其他异步框架)中,阻塞原语如 time.sleep()
替换为 asyncio.sleep()
等可等待对象,它暂停等待者并在适当的时候恢复它。其他协程和事件循环不仅不受协程暂停的影响,而且恰恰是它们有机会运行的时候。协程的挂起和恢复是 async-await 协作多任务的核心。
Asyncio 支持在单独的线程中运行遗留的阻塞函数,这样它们就不会阻塞事件循环。这是通过调用 run_in_executor
来实现的。这会将执行移交给线程池(用 Python 的 concurrent.futures
模块的说法是执行器)并返回 asyncio 等待:
async def f(x):
loop = asyncio.get_event_loop()
# start time.sleep(x) in a separate thread, suspend
# the current coroutine, and resume when it's done
await loop.run_in_executor(time.sleep, x)
这是 aiorequests 用来包装请求的阻塞函数的技术。原生异步函数,如 asyncio.sleep()
不要使用这种方法 ;它们直接告诉事件循环暂停它们以及如何唤醒它们(source)。run_in_executor
对于快速包装遗留阻塞代码非常有用且有效,仅此而已。由于以下几个原因,它总是不如原生异步实现:run_in_executor
,它共享线程的限制。run_in_executor
在后台使用线程池,因此如果您等待的函数多于最大工作线程数,则某些函数将不得不等待轮到它们才能开始工作。另一种方法是增加工作人员的数量,会用太多的线程淹没操作系统。 Asyncio 允许并行操作的数量与您在使用 poll
的手写状态机中所拥有的数量相匹配。监听事件。建议避免使用airequests之类的拐杖,直接潜入aiohttp . API 与请求的 API 非常相似,使用起来几乎一样愉快。
关于python-3.x - asyncio,将普通函数包装为异步,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57336602/