“asyncio.sleep(Delay)”是否保证睡眠至少需要“delay”秒?
问题描述
asyncio.sleep()
的阻塞表亲time.sleep()无法保证它将休眠请求的时间。
实际挂起时间可能小于请求的时间,因为任何捕获的信号都将在执行该信号的捕获例程后终止SLEEP()。
asyncio.sleep()
%sdocumentation没有提到类似的限制。
asyncio.sleep()
是否能够对休眠时间做出更强有力的保证?
解决方案
我不会说asyncio
保证,但根据实现可以得出结论:asyncio.sleep()
(基本上是call_later()
)在指定的时间间隔内休眠,但至少不精确到实现中使用的系统时钟的分辨率。
让我们把它弄清楚。首先,asyncio
使用单调时钟,其分辨率on different platforms不同(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
事件循环源code:
# 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
行end_time = self.time() + self._clock_resolution
显示回调可能比计划提前,但在时钟分辨率范围内。尤里·谢利万诺夫对此明确表示here:
在我看来,目前我们可以窥视未来的时间。我们为什么不这样做
end_time = self.time() - self._clock_resolution
是否保证超时总是在请求的时间之后而不是之前触发?如果我们这样做,我看不出性能会变得更差。
实际上,让我们运行下一个程序(Windows10上的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
相关文章