I need a sleep()
method which can be aborted (as described here or here).
My approach is to let a threading.Event.wait()
timeout at the specified duration:
def abortable_sleep(secs, abort_event):
abort_event.wait(timeout=secs)
abort_event.clear()
After calling abortable_sleep(10, _abort)
I can now (from another thread) call _event.set(_abort)
to let abortable_sleep()
terminate before the 10 seconds.
Example:
def sleeping_thread():
_start = time.perf_counter()
print("%f thread started" % (time.perf_counter() - _start))
abortable_sleep(5, _abort)
print("%f thread stopped" % (time.perf_counter() - _start))
if __name__ == '__main__':
_abort = threading.Event()
while True:
threading.Thread(target=sleeping_thread).start()
time.sleep(3)
_abort.set()
time.sleep(1)
Output:
0.000001 thread started
3.002668 thread stopped
0.000002 thread started
3.003014 thread stopped
0.000001 thread started
3.002928 thread stopped
0.000001 thread started
This code is working as expected but I still have some questions:
sleep()
which can be aborted?Event
instance which is not bound to an instance of abortable_sleep()
while True: abortable_sleep(0.0001)
? How is the wait()-timeout implemented?Due to race conditions, your solution is not always perfectly correct. You should use a threading.BoundedSemaphore()
instead. Call aquire()
immediately after creating it. When you want to sleep, call acquire()
with a timeout, then call release()
if the acquire()
returned true. To abort the sleep early, call release()
from a different thread; this will raise ValueError
if there is no sleep in progress.
Using an event instead is problematic if the other thread calls set()
at the wrong time (i.e. at any time other than when you are actually waiting on the event).
I have a wrapper class which basically slaps some sleep semantics on top of an Event
. The nice thing is that you only have to pass around a Sleep
object, which you can call sleep()
on several times if you like (sleep()
is not thread safe though) and that you can wake()
from another thread.
from threading import Event
class Sleep(object):
def __init__(self, seconds, immediate=True):
self.seconds = seconds
self.event = Event()
if immediate:
self.sleep()
def sleep(self, seconds=None):
if seconds is None:
seconds = self.seconds
self.event.clear()
self.event.wait(timeout=seconds)
def wake(self):
self.event.set()
Usage example:
if __name__ == '__main__':
from threading import Thread
import time
import logging
logger = logging.getLogger()
logger.setLevel(logging.DEBUG)
formatter = logging.Formatter('%(created)d - %(message)s')
handler = logging.StreamHandler()
handler.setFormatter(formatter)
logger.addHandler(handler)
logger.info("sleep")
s = Sleep(3)
logger.info("awake")
def wake_it(sleeper):
time.sleep(1)
logger.info("wakeup!")
sleeper.wake()
logger.info("sleeping again")
s = Sleep(60, immediate=False)
Thread(target=wake_it, args=[s]).start()
s.sleep()
logger.info("awake again")
The above might output something like this:
1423750549 - sleep
1423750552 - awake
1423750552 - sleeping again
1423750553 - wakeup!
1423750553 - awake again
Exactly what you did, but encapsulated in a class.
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