I am building a device based on raspberry PI. It will have several concurrent functions that should work simultaneously. In this case using asyncio looks like a reasonable choice (well, I can write all this stuff in C++ with threads, but python code looks much more compact)
One of the functions is to drive a stepper motor via GPIO pulses. These pulses should be 5-10 microseconds long. Is there a way to get asleep for a sub-milliseconds intervals with asyncio sleep?
On Linux asyncio uses the epoll_wait system call, which specifies the timeout in milliseconds, so anything sub-millisecond won't work, despite being able to specify it in asyncio. sleep() .
To sleep for milliseconds with this method, simply use a fractional number. To sleep for 400 milliseconds, for example, use time. sleep(0.4), use time for 60 milliseconds sleep(0.06), for example. Python's sleep() function is a part of the time package.
The Sleep() Function Of Asyncio In Python The asyncio. sleep() method suspends the execution of a coroutine. Coroutines voluntarily yield CPU leading to co-operative multitasking through the await keyword.
sleep(), it will ask the event loop to run something else while our await statement finishes its execution. Hence, you can see in the above code that await asyncio. sleep() is not blocking the execution of the script. Hence, while some part of our code is sleeping , the rest is being executed by the function.
Is there a way to get asleep for a sub-milliseconds intervals with asyncio sleep?
On Linux asyncio uses the epoll_wait
system call, which specifies the timeout in milliseconds, so anything sub-millisecond won't work, despite being able to specify it in asyncio.sleep()
.
You can test it on your machine by running the following program:
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())
Save the program e.g. as sleep1.py
and run it under strace
, like this:
$ strace -fo trc -T python3.7 sleep1.py
<wait a second or two, then press Ctrl-C to interrupt>
The trc
file will contain reasonably precise timings of what goes on under the hood. After the Python startup sequence, the program basically does the following in an infinite loop:
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>
We see a call to getpid()
, two calls to epoll_wait
, and finally the call to stat
. The first epoll_wait
is actually relevant, it specifies the timeout in milliseconds, and sleeps for approximately the desired period. If we lower the sleep duration to sub-milliseconds, e.g. 100e-6, strace
shows that asyncio still requests a 1ms timeout from epoll_wait
, and gets as much. The same happens with timeouts down to 15 us. If you specify a 14 us or smaller timeout, asyncio actually requests a no-timeout poll, and epoll_wait
completes in 8 us. However, the second epoll_wait
also takes 8 us, so you can't really count on microsecond resolution in any form of shape.
Even if you use threads and busy-looping, you are likely to encounter synchronization issues with the GIL. This should likely done in a lower-level language such as C++ or Rust, and even so you'll need to be careful about the OS scheduler.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With