我正在一遍又一遍地阅读卢西亚诺·拉马略的“流利的Python”,但我无法理解异步。睡眠在异步中的行为。
书中有一个部分说:
除非您想阻止主线程,否则永远不要在异步协同中使用time.sleep,因此冻结事件循环,可能也冻结整个应用程序。(...)它应该从asyncio.sleep(延迟)中屈服。
在另一部分:
Python标准库中的每个阻塞I/O函数都释放GIL (.)time.sleep()函数还释放GIL。
由于time.sleep()可以在其他线程上释放GIL代码,但会阻塞当前线程。由于异步是单线程的,我理解time.sleep阻塞异步循环。
但是,asyncio.sleep()如何不阻塞线程呢?是否可以不延迟事件循环并同时等待?
发布于 2020-06-21 02:57:08
函数asyncio.sleep
只是秒而time.sleep
秒。
您可以用这个小示例测试两者的行为,看看asyncio.sleep(1)
是如何不给出它将“睡眠”的时间的,因为这并不是它真正做的事情:
import asyncio
import time
from datetime import datetime
async def sleep_demo():
print("async sleep start 1s: ", datetime.now().time())
await asyncio.sleep(1)
print("async sleep end: ", datetime.now().time())
async def I_block_everyone():
print("regular sleep start 3s: ", datetime.now().time())
time.sleep(3)
print("regular sleep end: ", datetime.now().time())
asyncio.gather(*[sleep_demo(), I_block_everyone()])
这些指纹:
async sleep start 1s: 04:46:55
regular sleep start 3s: 04:46:55
regular sleep end: 04:46:58
async sleep end: 04:46:58
阻塞调用time.sleep
阻止事件循环调度恢复sleep_demo
的未来。最后,它仅在大约3秒后才获得控制权(尽管我们明确请求1秒异步睡眠)。
现在关于“time.sleep()
函数也释放了GIL”,这并不矛盾,因为它只允许执行另一个线程(但是当前线程将在x
秒内保持挂起)。两者看起来有点相似,在一种情况下,释放GIL是为了为另一个线程腾出空间,在asyncio.sleep
中,事件循环获得控制权以调度另一个任务。
发布于 2020-06-21 02:46:40
在幕后,asyncio
有一个“事件循环”:它是一个在任务队列上循环的函数。添加新任务时,它将添加到队列中。当任务产生时,它被挂起,事件循环移到下一个任务。挂起的任务将被忽略,直到它们恢复为止。当任务完成时,它将从队列中移除。
例如,当您调用asyncio.run
时,它会将新任务添加到队列中,然后进入事件循环,直到没有其他任务为止。
很少引用官方文件的话:
事件循环是每个异步应用程序的核心。事件循环运行异步任务和回调,执行网络IO操作,并运行子进程。
事件循环使用协作调度:事件循环一次运行一个任务。任务等待未来的完成时,事件循环运行其他任务、回调或执行IO操作。
调用asyncio.sleep
时,它将挂起当前任务,从而允许运行其他任务。好吧,我基本上是在重述文档
睡眠()总是挂起当前任务,允许运行其他任务。
https://stackoverflow.com/questions/62493718
复制相似问题