i am working on a project that has the following requirements:
- critical function runs at exactly a 10ms interval and burps out anything it finds
- run other functions every 1 second(+/- 100ms)
- send report every 5 seconds(+/- 100ms)
the astute among you may rightly assume MQTT is in the mix, but i didn’t want to muddy the waters. as it stands, all relevant functions work on their own. my challenge is threading these such that the critical function runs exactly as required while outputting values for the report function. ditto for the other functions, but they can fire/run/dump and then be relaunched. the critical one must always be running.
i have no idea how to thread these correctly. i think threading
is the best way to go since i am I/O bound and the I/O is pretty fast. the kink is that i’ve come across a resolution-perfect clock function that i’d like to incorporate, but apparently it is only good with asyncio
. while i am not a stranger to Python, i am a stranger to threading in Python. so please bear with me.
i have been advised to use three threads: on for mission critical function, one for the other functions, and a final third for the reporting. all well and good… but i am stuck with some basic understanding of how to fit this into a while True
event loop.
i appreciate this is a bit of a barn-door-ask, but i am stumped and not sure where to turn to. i am open to whatever refinements to this post are required to move this puck forward.
here’s what i’m tinkering with using that timing clock:
import time
import asyncio
goState = True
tickCount = 0
def do_every(periodInSeconds, fx, *args):
global goState
def g_tick():
t = time.time()
while True:
t += periodInSeconds
yield max(t - time.time(), 0)
g = g_tick()
while goState:
time.sleep(next(g))
fx(*args)
def checkIO(s, loop):
global tickCount
tt = str(time.time())[6:15] #<-- trim values to the ms
print(f'hello {s} {tickCount} {goState} ({tt})')
tickCount += 1
time.sleep(.009) #<-- simulation of I/O sampling process
if not goState:
loop.stop()
loop = asyncio.get_event_loop()
loop.call_soon(do_every, 0.01, checkIO, 'fleep', loop) #<-- 'fleep' = input to func
#
# i know this ↓ gets called right away, but i'm not sure how to loop this
# properly. you can see what it's SUPPOSED to do... and i'm not sure
# where to add the other necessary threads for reporting and other
# sampling functions (the other two threads).
#
print(f'current tickCount: {tickCount}')
if tickCount >= 200:
print(f'turning off the machine')
goState = False #<-- should break the do_every() loop...?
loop.run_forever()
loop.close()
i have a bunch of other files that i’ve been bashing away at, but this is the once that feels the closest and incorporates the accurate clock.
EDIT: one further kink i meant to mention, the threads need to be cancellable. which i presume is not a problem since we can name threads and subsequently call thread_x.cancel()
to do that. if i’m wrong, please let me know. i was also looking at the second coroutine
implementation found in this post.
EDIT2: i mistyped the interval for the critical loop, so i corrected it (100ms → 10ms)