python - `asyncio.sleep(delay)` 是否保证睡眠至少 `delay` 秒?
问题描述
asyncio.sleep()
的阻塞表亲time.sleep()不能保证它会在请求的时间内休眠。
实际的挂起时间可能少于请求的时间,因为任何捕获的信号都会在执行该信号的捕获例程后终止 sleep()。
asyncio.sleep()
的文档没有提到类似的限制。
asyncio.sleep()
能够对睡眠时间做出更有力的保证吗?
解决方案
我不会说什么asyncio
保证,但根据实现,asyncio.sleep()
(基本上,call_later()
)睡眠指定的时间间隔,但至少不准确等于实现中使用的系统时钟的分辨率。
让我们弄清楚。首先,asyncio
使用单调时钟,在不同平台上具有不同的分辨率(Python 和 OS 分辨率)。例如,Windows
这与15ms
.
在保证方面,请注意对函数的注释BaseEventLoop.time
:
def time(self):
"""Return the time according to the event loop's clock.
This is a float expressed in seconds since an epoch, but the
epoch, precision, accuracy and drift are unspecified and may
differ per event loop.
"""
return time.monotonic()
现在让我们看一下负责启动定时定时器的asyncio
事件循环源码:
# Handle 'later' callbacks that are ready.
end_time = self.time() + self._clock_resolution
while self._scheduled:
handle = self._scheduled[0]
if handle._when >= end_time:
break
handle = heapq.heappop(self._scheduled)
handle._scheduled = False
self._ready.append(handle)
行end_time = self.time() + self._clock_resolution
显示回调可能比计划的更早触发,但在时钟分辨率内。Yuri Selivanov在这里明确说明了这一点:
在我看来,目前我们正在窥视未来的时间。我们为什么不做
end_time = self.time() - self._clock_resolution
保证超时总是在请求时间之后触发,而不是之前?如果我们这样做,我看不出性能会如何变得更糟。
真的,让我们运行下一个程序(Windows 10 上的 Python 3.8):
import asyncio
import time
async def main():
print("Timer resolution", time.get_clock_info('monotonic').resolution)
while True:
asyncio.create_task(asyncio.sleep(1))
t0 = time.monotonic()
await asyncio.sleep(0.1)
t1 = time.monotonic()
print(t1 - t0)
asyncio.run(main())
我们看到上述行为:
Timer resolution 0.015625
0.09299999987706542
0.0940000000409782
0.0940000000409782
0.10900000017136335
...
但是在正文的开头,我至少说了时钟分辨率,因为asyncio
在协作多任务处理的条件下工作,并且如果有一个贪婪的协程(或许多不那么贪婪的协程)不会太频繁地控制事件循环,我们有以下图片:
import asyncio
import time
async def calc():
while True:
k = 0
for i in range(1*10**6):
k += i
await asyncio.sleep(0.1) # yield to event loop
async def main():
asyncio.create_task(calc()) # start greedy coroutine
print("Timer resolution", time.get_clock_info('monotonic').resolution)
while True:
asyncio.create_task(asyncio.sleep(1))
t0 = time.monotonic()
await asyncio.sleep(0.1)
t1 = time.monotonic()
print(t1 - t0)
asyncio.run(main())
不出所料,这种情况正在向增加延迟的方向发生变化:
0.17200000025331974
0.1559999999590218
0.14100000029429793
0.2190000000409782
推荐阅读
- angular - 如何以编程方式在指定位置注入组件
? - r - 循环用于在 R 的 ggenealogy 包中包含列表而不是个人
- telegram - 如何删除 Telegram 应用程序(api_key & api_hash)?
- formula - Libreoffice Calc 沿行复制公式而不偏移函数列变量
- opencv - Python 2.7:cv2 (opencv) DLL 加载失败:找不到指定的过程。(Windows XP)
- react-native - 无法从“node_modules\antd-mobile\lib\button\style\css.js”解析模块“./index.css”
- python - Django:表单成功但图片未上传
- linux - 汇编器编译的问题
- bash - 通过带有计数器的模式在bash中解析多个CSV文件
- javascript - 动态注入具有@font-face 的样式表会导致不可见文本闪烁