Sleeping threads using events in python

So today i needed to find a way to sleep() a thread in python without blocking the other threads. For some reason, time.sleep() wasn’t cutting it and was blocking my main thread.

Well, unfortunately python dosen´t have a thread.sleep() (as far as i can tell) so we need to find a way around that.

Enter Timer and Event.

Event is a simple, thread-safe object that represents an (you guessed it) event. This object can be set() to signal that the event occurred and, more importantly, it can be waited by other threads until it’s set().

The Timer is a little more complicated. The Timer is a subclass of thread and is used when we want to execute and action after a set period of time. The Timer class takes at least two arguments, the time to wait (sleep… 😉 ) and the callback function that will be called after the time runs out. We can also pass a list of arguments that will be then passed to the callback function.

So now we have everything we need. An object that can be waited by some threads and set() by another and an object that will execute an action (inside another thread but that is not relevant for now) after a preset period of time.

The solution is now simple. In the thread you want to sleep, create the event, create the timer and pass the event as an argument to the callback function, start() the timer and wait() for the event to be set by the callback.

Let´s first create the event object:

event = Event()

Easy right? Now let’s create the function that will be called back when the timer is over:

def timeout_callback(event):
    event.set() # set's the event

Alright, now the timer with the event that needs to be passed to the callback and start it:

t = Timer(5.0, timeout_callback, [event])
t.start()

Finally, let´s make our thread wait() for the event to be set:

event.wait()

Now our thread will block on the wait() call releasing the processor to the other threads so that they can go on about their business. When the timer reaches 0, the callback will be called, the event set, and our thread will return from the wait() call and continue doing it’s thing.

To put it all together (wraped in a convenient function):

from threading import Timer,Event

def timeout_callback(event):
    event.set() # set's the event

def wait_time(time):
	# Sleeps the thread for the given time (in seconds) whithout
	# blocking other threads
	event = Event()
	t = Timer(time, timeout_callback, [event])
	t.start()
	# Wait whithout blocking
	event.wait()
	print('Event set!')

wait_time(5.0) # wait for 5 seconds

BEWARE:

For this particular case, the event object must be unique to each thread, otherwise, if it’s shared by multiple threads, once one thread set’s the event, all other threads wainting on it will unblock and continue their work before their time runs out

Advertisements
This entry was posted in Knowledge and tagged , , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s