When working with multiple processes in Python, sometimes one process needs to tell others to start or stop doing something. This is called signaling. The multiprocessing.Event
is a simple tool that lets one process send a signal, and other processes wait for that signal before continuing.
In this article, you will learn how to create an Event
, how to set it to send a signal, clear it to reset the signal, and how multiple processes can use it to work together smoothly.
Setting Up: Imports and Creating an Event
First, you need to import the Event
class from Python’s multiprocessing
module, along with time
for simulating delays. Creating an Event is simple—you just call Event()
to make a new event object.
Imagine a traffic light system where cars wait at a red light. The Event
will act as the green light signal: when set, cars can go; when cleared, they must wait.
Here is how to create the event:
from multiprocessing import Event
event = Event()
This event
starts in a cleared state, meaning no signal is sent yet. Processes can check or wait for this signal before moving on.
Waiting for an Event in a Process
Processes can pause and wait until an event is set by calling the .wait()
method on the Event
object. When .wait()
is called, the process blocks until another process sets the event, signaling that it can continue.
For example, imagine cars waiting at a traffic light. Each car process waits for the green light event before it moves.
Here is how you might write this:
def car(name, event):
print(f"{name} waiting for green light...")
event.wait() # Wait here until event is set
print(f"{name} goes!")
In this example, each car prints a message saying it is waiting. When the event is set, all waiting cars proceed and print that they are going.
Setting and Clearing the Event
One process can control the flow by setting the event to signal other processes to proceed. Later, it can clear the event to make others wait again.
For example, a traffic controller turns the green light on by setting the event, allowing cars to go. After some time, the controller switches the light to red by clearing the event, stopping the cars.
Here’s how it looks in code:
def traffic_light(event):
print("Green light on!")
event.set() # Signal cars to go
time.sleep(5) # Keep green light on for 5 seconds
print("Red light!")
event.clear() # Signal cars to stop
This way, the event acts like a traffic light signal controlling multiple processes.
Starting and Coordinating Multiple Processes
In this section, we’ll bring the traffic light and cars together. We’ll start multiple car processes, each waiting for the green light event. Meanwhile, a traffic light process controls when the event is set or cleared.
All cars will wait until the traffic light sets the event to green. Once the event is set, they proceed to “go.” This coordination ensures cars only move when allowed.
Here’s a complete example tying everything together:
from multiprocessing import Process, Event
import time
import random
def car(name, event):
print(f"{name} waiting for green light...")
event.wait() # Wait here until event is set
print(f"{name} goes!")
def traffic_light(event):
print("Green light on!")
event.set() # Signal cars to go
time.sleep(5) # Keep green light on for 5 seconds
print("Red light!")
event.clear() # Signal cars to stop
if __name__ == "__main__":
event = Event()
# Create car processes
cars = [Process(target=car, args=(f"Car {i+1}", event)) for i in range(3)]
# Start all car processes
for c in cars:
c.start()
# Start traffic light process
light = Process(target=traffic_light, args=(event,))
light.start()
# Wait for all cars to finish
for c in cars:
c.join()
# Wait for traffic light process to finish
light.join()
This example clearly shows how processes synchronize with Event
: cars wait patiently, then move together once the green light is on.
Timeouts and Checking Event Status
Sometimes, a process should wait for a signal only for a limited time. The .wait(timeout)
method lets a process wait for the event but stops waiting after the timeout seconds if the event hasn’t been set.
You can also check if the event is currently set anytime using .is_set()
. This helps processes decide what to do next based on the event’s status.
Here’s a simple example demonstrating both:
from multiprocessing import Event
import time
event = Event()
# Wait up to 3 seconds for the event to be set
if event.wait(timeout=3):
print("Green light received.")
else:
print("Timed out waiting.")
# Later, check if event is set
if event.is_set():
print("Event is currently set.")
else:
print("Event is not set right now.")
This way, processes can handle cases where waiting forever isn’t ideal, and can check the signal status whenever needed.
Fun Example: Space Launch Countdown
Imagine a space launch where multiple systems—like navigation, engines, and communication—must all wait for the official launch signal before starting their tasks. In this example, the launch control process counts down from five, then signals all systems to begin by setting an event. Each system process waits patiently for this signal, and once it arrives, they start their operations simultaneously.
This simple scenario demonstrates how multiprocessing.Event
lets multiple processes coordinate by waiting for and responding to a single signal, all while keeping the code clear and engaging.
from multiprocessing import Process, Event
import time
def system(name, launch_event):
print(f"{name} waiting for launch signal...")
launch_event.wait()
print(f"{name} starting operations!")
def launch_control(launch_event):
print("Launch countdown starting...")
for i in range(5, 0, -1):
print(f"T-minus {i} seconds")
time.sleep(1)
print("Launch signal given!")
launch_event.set()
if __name__ == "__main__":
launch_event = Event()
systems = ['Navigation', 'Engines', 'Communication']
processes = [Process(target=system, args=(name, launch_event)) for name in systems]
for p in processes:
p.start()
launch_control(launch_event)
for p in processes:
p.join()
The code creates a shared Event
object called launch_event
. Each system process runs a function that prints a waiting message, then calls launch_event.wait()
to pause until the launch signal is given. The launch control process performs the countdown with timed print statements and finally calls launch_event.set()
to release all waiting processes at once. After the signal, each system prints that it is starting.
Using Event for Simple Synchronization
You can also use multiprocessing.Event
as a straightforward way to synchronize two processes. For example, one process performs a task and then signals when it is done by setting the event. Meanwhile, the main process waits for this signal before continuing.
This pattern is useful when you want to ensure that a task finishes before moving on, without constantly checking or polling.
from multiprocessing import Process, Event
import time
def worker(task_done_event):
print("Worker: Starting task...")
time.sleep(3) # Simulate a time-consuming task
print("Worker: Task completed.")
task_done_event.set() # Signal task completion
if __name__ == "__main__":
task_done_event = Event()
p = Process(target=worker, args=(task_done_event,))
p.start()
print("Main process: Waiting for worker to finish...")
task_done_event.wait() # Wait until the worker signals completion
print("Main process: Worker has finished the task.")
p.join()
In this example, the worker process simulates doing some work by sleeping for three seconds. After finishing, it calls task_done_event.set()
to notify the main process that the task is complete. The main process waits on task_done_event.wait()
, blocking until the event is set. This simple synchronization avoids unnecessary busy-waiting and keeps the coordination clean and clear.
Cleaning Up: Joining Processes
After using events to coordinate processes, it’s important to properly clean up by joining all child processes. This ensures that each process finishes its work before the main program exits, preventing any orphaned or zombie processes.
Here’s a simple example showing how to start, coordinate, and then join multiple processes using an event:
from multiprocessing import Process, Event
import time
def worker(name, event):
print(f"{name} waiting for start signal...")
event.wait()
print(f"{name} started working!")
time.sleep(2)
print(f"{name} finished work.")
if __name__ == "__main__":
start_event = Event()
workers = ['Worker1', 'Worker2', 'Worker3']
processes = [Process(target=worker, args=(name, start_event)) for name in workers]
for p in processes:
p.start()
print("Main process: Giving start signal.")
start_event.set()
for p in processes:
p.join()
print("All worker processes have finished.")
This code creates three worker processes that wait for a start signal via the event. The main process sets the event to let them run, then calls .join()
on each process to wait for their completion. Finally, it prints a message confirming all workers have finished. Using .join()
is the clean way to end multiprocessing programs coordinated by events.
Conclusion
multiprocessing.Event
provides a simple and effective way for processes to signal each other and coordinate their actions. By allowing one process to set a flag that others can wait on, it makes synchronization clear and easy to manage. Whether you’re building games, running simulations, or handling any task where processes must wait for a signal, Event
is a handy tool to keep your multiprocessing code organized and responsive. Give it a try to experience how smooth inter-process signaling can be.