有没有办法通过异步睡眠以亚毫秒的间隔入睡?
在 Linux 上 asyncio 使用epoll_wait
系统调用,它以毫秒为单位指定超时,因此任何亚毫秒的内容都不起作用,尽管可以在asyncio.sleep()
.
您可以通过运行以下程序在您的计算机上对其进行测试:
import asyncio, os
SLEEP_DURATION = 5e-3 # 5 ms sleep
async def main():
while True:
# suspend execution
await asyncio.sleep(SLEEP_DURATION)
# execute a syscall visible in strace output
os.stat('/tmp')
asyncio.run(main())
保存程序,例如作为sleep1.py
并在下面运行它strace
, 像这样:
$ strace -fo trc -T python3.7 sleep1.py
<wait a second or two, then press Ctrl-C to interrupt>
The trc
文件将包含幕后发生的事情的相当精确的时间。在Python启动序列之后,程序基本上在无限循环中执行以下操作:
24015 getpid() = 24015 <0.000010>
24015 epoll_wait(3, [], 1, 5) = 0 <0.005071>
24015 epoll_wait(3, [], 1, 0) = 0 <0.000010>
24015 stat("/tmp", {st_mode=S_IFDIR|S_ISVTX|0777, st_size=45056, ...}) = 0 <0.000014>
我们看到一个电话getpid()
, 两次调用epoll_wait
,最后调用stat
。首先epoll_wait
实际上是相关的,它指定超时(以毫秒为单位),并休眠大约所需的时间。如果我们将睡眠持续时间降低到亚毫秒,例如100e-6,strace
显示 asyncio 仍然请求 1ms 超时epoll_wait
,并得到同样多的东西。当超时时间降至 15 us 时也会发生同样的情况。如果您指定 14 us 或更小的超时,asyncio 实际上会请求无超时轮询,并且epoll_wait
8 我们内完成。然而,第二个epoll_wait
也需要 8 us,所以你不能真正指望任何形式的微秒分辨率。
即使您使用线程和忙循环,您也可能会遇到 GIL 的同步问题。这可能应该用 C++ 或 Rust 等较低级语言来完成,即使如此,您也需要小心操作系统调度程序。