异步:睡眠时间少于毫秒

时间:2019-02-02 20:23:33

标签: python python-asyncio

我正在构建基于树莓派PI的设备。它将具有多个应同时运行的并发功能。在这种情况下,使用asyncio似乎是一个合理的选择(嗯,我可以使用C ++用线程编写所有这些内容,但是python代码看起来要紧凑得多)

功能之一是通过GPIO脉冲驱动步进电机。这些脉冲应为5-10微秒长。有没有一种方法可以使异步睡眠在亚毫秒间隔内入睡?

1 个答案:

答案 0 :(得分:1)

  

是否有一种方法可以使异步睡眠在亚毫秒间隔内入睡?

在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>

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仍然从epoll_wait请求1ms超时,并且得到了更多。超时降至15 us时也会发生同样的情况。如果您指定14 us或更短的超时时间,那么asyncio实际上会请求无超时轮询,并且epoll_wait将在8 us之内完成。但是,第二个epoll_wait也需要8个小时,因此您不能真正依靠任何形式的微秒分辨率。

即使使用线程和繁忙循环,您也很可能会遇到GIL的同步问题。这应该使用较低级的语言(例如C ++或Rust)来完成,即使这样,您也需要注意操作系统调度程序。