asyncio:以亚毫秒为间隔休眠

Ole*_*nko 5 python python-asyncio

我正在构建基于树莓派的设备。它将有几个应该同时工作的并发功能。在这种情况下,使用 asyncio 看起来是一个合理的选择(好吧,我可以用 C++ 用线程编写所有这些东西,但 python 代码看起来更紧凑)

其中一项功能是通过 GPIO 脉冲驱动步进电机。这些脉冲的长度应为 5-10 微秒。有没有办法在 asyncio sleep 的亚毫秒间隔内入睡?

use*_*342 5

有没有办法在 asyncio sleep 的亚毫秒间隔内入睡?

在 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())
Run Code Online (Sandbox Code Playgroud)

将程序另存为sleep1.py并在 下运行strace,如下所示:

$ strace -fo trc -T python3.7 sleep1.py
<wait a second or two, then press Ctrl-C to interrupt>
Run Code Online (Sandbox Code Playgroud)

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>
Run Code Online (Sandbox Code Playgroud)

我们看到一个调用到getpid(),两个调用到epoll_wait,最后调用到stat。第一个epoll_wait实际上是相关的,它以毫秒为单位指定超时,并休眠大约所需的时间。如果我们将睡眠持续时间降低到亚毫秒,例如 100e-6,strace表明 asyncio 仍然从 请求 1ms 超时epoll_wait,并获得同样多的超时。超时低至 15 us 时也会发生同样的情况。如果您指定 14 us 或更小的超时,asyncio 实际上请求无超时轮询,并epoll_wait在 8 us 内完成。然而,第二个epoll_wait也需要 8 个我们,所以你不能真正指望任何形状的微秒分辨率。

即使您使用线程和忙循环,您也可能会遇到 GIL 的同步问题。这可能应该使用 C++ 或 Rust 等低级语言完成,即使如此,您也需要小心操作系统调度程序。