异步:休眠亚毫秒间隔
问题描述
我正在构建一个基于覆盆子PI的设备。它将有几个应该同时工作的并发功能。在这种情况下,使用Asyncio看起来是一个合理的选择(嗯,我可以用C++用线程编写所有这些东西,但是Python代码看起来要紧凑得多)
功能之一是通过GPIO脉冲驱动步进电机。这些脉冲应为5-10微秒长。有没有办法在异步睡眠的情况下在亚毫秒间隔内入睡?解决方案
有没有办法使用异步睡眠在亚毫秒间隔内入睡?
在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秒的时候。如果您指定14 us或更小超时,则Asyncio实际上请求无超时轮询,epoll_wait
在8 us内完成。但是,第二个epoll_wait
也需要8微秒,所以您不能指望任何形式的微秒分辨率。
即使您使用线程和繁忙循环,也很可能会遇到GIL的同步问题。这很可能是在低级语言(如C++或Rust)中完成的,即使如此,您也需要小心操作系统调度程序。
相关文章