Threading Event Object In Python - Super Fast Python

Last Updated on September 12, 2022

You can use an Event Object in Python via the threading.Event class.

In this tutorial you will discover how to use an event object in Python.

Let’s get started.

Need for an Event Object

A thread is a thread of execution in a computer program.

Every Python program has at least one thread of execution called the main thread. Both processes and threads are created and managed by the underlying operating system.

Sometimes we may need to create additional threads in our program in order to execute code concurrently.

Python provides the ability to create and manage new threads via the threading module and the threading.Thread class.

You can learn more about Python threads in the guude:

In concurrent programming in threads, sometimes we need to coordinate threads with a boolean variable. This might be to trigger an action or signal some result.

This could be achieved with a mutual exclusion lock (mutex) and a boolean variable, but provides no way for threads to wait for the variable to be set True.

Instead, this can be achieved using an event object.

What is an event object and how can we use it in Python?

How to Use an Event Object

Python provides an event object via the threading.Event class.

An event is a simple concurrency primitive that allows communication between threads.

A threading.Event object wraps a boolean variable that can either be “set” (True) or “not set” (False). Threads sharing the event instance can check if the event is set, set the event, clear the event (make it not set), or wait for the event to be set.

The threading.Event provides an easy way to share a boolean variable between threads that can act as a trigger for an action.

This is one of the simplest mechanisms for communication between threads: one thread signals an event and other threads wait for it.

Event Objects, threading — Thread-based parallelism

First, an event object must be created and the event will be in the “not set” state.

...

# create an instance of an event

event = threading.Event()

Once created we can check if the event has been set via the is_set() function which will return True if the event is set, or False otherwise.

For example:

...

# check if the event is set

if event.is_set():

# do something...

The event can be set via the set() function. Any threads waiting on the event to be set will be notified.

For example:

...

# set the event

event.set()

The event can be marked as “not set” (whether it is currently set or not) via the clear() function.

...

# mark the event as not set

event.clear()

Finally, threads can wait for the event to set via the wait() function. Calling this function will block until the event is marked as set (e.g. another thread calling the set() function). If the event is already set, the wait() function will return immediately.

...

# wait for the event to be set

event.wait()

From reviewing the source code for threading.Event, waiting threads are only notified when the set() function is called, not when the clear() function is called.

A “timeout” argument can be passed to the wait() function which will limit how long a thread is willing to wait in seconds for the event to be marked as set.

The wait() function will return True if the event was set while waiting, otherwise a value of False returned indicates that the event was not set and the call timed-out.

...

# wait for the event to be set with a timeout

event.wait(timeout=10)

Now that we know how to use a threading.Event, let’s look at a worked example.

Example of Using an Event Object

We can explore how to use a threading.Event object.

In this example we will create a suite of threads that each will perform some processing and report a message. All threads will use an event to wait to be set before starting their work. The main thread will set the event and trigger the processing in all threads.

First, we can define a target task function that takes the shared threading.Event instance and a unique integer to identify the thread.

# target task function

def task(event, number):

# ...

Next, the function will wait for the event to be set before starting the processing work.

...

# wait for the event to be set

event.wait()

Once triggered, the thread will generate a random number, block for a moment and report a message.

...

# begin processing

value = random()

sleep(value)

print(f'Thread {number} got {value}')

Tying this together, the complete target task function is listed below.

# target task function

def task(event, number):

    # wait for the event to be set

    event.wait()

    # begin processing

    value = random()

    sleep(value)

    print(f'Thread {number} got {value}')

The main thread will first create the shared threading.Event instance, which will be in the “not set” state by default.

...

# create a shared event object

event = Event()

Next, we can start five new threads specifying the target task() function with the event object and a unique integer as arguments.

...

# create a suite of threads

for i in range(5):

    thread = Thread(target=task, args=(event, i))

    thread.start()

Finally, the main thread will block for a moment, then trigger the processing in all of the threads via the event object.

...

# block for a moment

print('Main thread blocking...')

sleep(2)

# start processing in all threads

event.set()

Tying this all together, the complete example is listed below.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

# SuperFastPython.com

# example of using an event object

from time import sleep

from random import random

from threading import Thread

from threading import Event

# target task function

def task(event, number):

    # wait for the event to be set

    event.wait()

    # begin processing

    value = random()

    sleep(value)

    print(f'Thread {number} got {value}')

# create a shared event object

event = Event()

# create a suite of threads

for i in range(5):

    thread = Thread(target=task, args=(event, i))

    thread.start()

# block for a moment

print('Main thread blocking...')

sleep(2)

# start processing in all threads

event.set()

# wait for all the threads to finish...

Running the example first creates and starts five threads. Each thread waits on the event before it starts processing.

The main thread blocks for a moment, allowing all threads to get started and start waiting on the event. The main thread then sets the event. This triggers all five threads that perform their processing and report a message.

Note, your specific results will differ given the use of random numbers.

Main thread blocking...

Thread 2 got 0.12310258010408259

Thread 4 got 0.2951602689055347

Thread 3 got 0.2995528547475702

Thread 0 got 0.6661975710559368

Thread 1 got 0.6687449622581129


Free Python Threading Course

Download your FREE threading PDF cheat sheet and get BONUS access to my free 7-day crash course on the threading API.

Discover how to use the Python threading module including how to create and start new threads and how to use a mutex locks and semaphores

Learn more
 


Further Reading

This section provides additional resources that you may find helpful.

Python Threading Books

I also recommend specific chapters in the following books:

  • Python Cookbook, David Beazley and Brian Jones, 2013.
    • See: Chapter 12: Concurrency
  • Effective Python, Brett Slatkin, 2019.
    • See: Chapter 7: Concurrency and Parallelism
  • Python in a Nutshell, Alex Martelli, et al., 2017.
    • See: Chapter: 14: Threads and Processes

Guides

APIs

References

Takeaways

You now know how to use a threading.Event Object in Python

Do you have any questions?
Ask your questions in the comments below and I will do my best to answer.

Photo by Harley-Davidson on Unsplash