0

Fairly new to Python; working on a Raspberry Pi 4 with Python 3.4.3.

Got a code working to listen for 2 distinct alarms in my lab - one for a -80 freezer getting too warm, and the other for a -20 freezer. Code listens on a microphone, streams data, Fourier-transforms it, detects the peaks I'm interested in, and triggers events when they're found - eventually going to email me and my team if an alarm goes off, but still just testing with Print commands atm. Let's call them Alarm A/EventA and Alarm B/Event B.

I want it to trigger Event A when Alarm A is detected, but then wait 1 hour before triggering Event A again (if Alarm A is still going off/goes off again in an hour). Meanwhile, though, I also want it to continue listening for Alarm B and trigger Event B if detected - again, only once per hour.

Since I can't just do time.sleep, then, I'm trying to do it with Threads - but am having trouble starting, stopping, and restarting a thread for the 1 hour (currently just 10 second for testing purposes) delay.

I have variables CounterA and CounterB set to 0 to start. When Alarm A is detected I have the program execute EventA and up CounterA to 1; ditto for AlarmB/EventB/CounterB. EventA and EventB are only triggered if CounterA and CounterB are <1.

I'm having a real hard time resetting the counters after a time delay, though. Either I end up stalling the whole program after an event is triggered, or I get the error that threads can only be started once. Here are the relevant sections of the code:

import time
import threading

CounterA = 0
CounterB = 0

def Aresetter():
    time.sleep(10)
    global CounterA
    CounterA=CounterA-1
    thA.join()

def Bresetter():
    time.sleep(10)
    global CounterB
    CounterB=CounterB-1
    thB.join()

thA = threading.Thread(target = Aresetter)

thB = threading.Thread(target = Bresetter)

if any(#Alarm A detection) and CounterA<1:
    print('Alarm A!')
    CounterA=CounterA+1
    thA.start()
elif any(#Alarm B detection) and CounterB<1:
    print('Alarm B!')
    CounterB=CounterB+1
    thB.start()
else:
    pass

I think the crux of my problem is I can't have the resetter functions join the threads to main once they're finished with their delayed maths - but I also don't know how to do that in the main program without making it wait for the same amount of time and thus stalling everything...

martineau
  • 119,623
  • 25
  • 170
  • 301
  • FYI you can’t externally stop or pause a thread. It can exit, and a common pattern is for the thread to monitor a variable/queue which tells it to pause/resume or to exit. Restart could be included in the code you would write to handle this variable telling the thread what to do - your code will have to make sure the thread checks this variable often enough for your needs. – DisappointedByUnaccountableMod Aug 03 '21 at 22:02

3 Answers3

2

You don't need threads for this at all.

Just keep track of the last time (time.time()) you triggered each alarm, and don't trigger them if less than 60 minutes (or whatever the threshold is) has elapsed since the last time.

Something like (semi pseudocode)...

import time

last_alarm_1 = 0 # a long time ago, so alarm can trigger immediately 
# ...
if alarm_1_cond_met():
    now = time.time()
    if now - last_alarm_1 > 60 * 60:  # seconds
        send_alarm_1_mail()
        last_alarm_1 = now

Repeat for alarm 2 :)

AKX
  • 152,115
  • 15
  • 115
  • 172
0

AKX has a better solution to your problem, but you should be aware of what this does when Aresetter() is called by the thA thread:

def Aresetter():
    ...
    thA.join()

The thA.join() method doesn't do anything to the thA thread. All it does is, it waits for the thread to die, and then it returns. But, if it's the thA thread waiting for itself to die, it's going to be waiting for a very long time.

Solomon Slow
  • 25,130
  • 5
  • 37
  • 57
0

Also, there's this:

How to...restart a thread?

You can't. I don't want to explore why it makes any sense, but you just can't do that. It's not how threads work. If you want your program to do the same task more than one time "in another thread," you have a couple of options:

  1. Create a new thread to do the task each time.

  2. Create a single thread that does the same task again and again, possibly sleep()ing in between, or possibly awaiting some message/signal/trigger before each repetition.

  3. Submit a task to a thread pool* each time you want the thing to be done.

Option (2) could be better than option (1) because creating and destroying threads is a lot of work. With option (2) you're only doing that once.

Option (1) could be better than option (2) because threads use a significant amount of memory. If the thread doesn't exist when it's not needed, then that memory could be used by something else.

Option (3) could be better than the both of them if the same thread pool is also used for other purposes in your program. The marginal cost of throwing a few more tasks at an already-existing thread pool is trivial.


* I don't know that Python has a ready-made, first-class ThreadPool class for you to use. It has this, https://stackoverflow.com/a/64373926/801894 , but I've never used it. It's not that hard though to create your own simple thread pool.

Solomon Slow
  • 25,130
  • 5
  • 37
  • 57