SimPy Events Explained: How Things Happen in Simulation
Events are the heartbeat of SimPy. Every time something happens—a timeout expires, a resource becomes available, a process completes—that's an event.
What Is an Event?
An event is something that will occur at a specific point in simulation time. Processes yield events to pause and wait for them.
event = env.timeout(5) # Event that occurs in 5 time units
yield event # Wait for it
Event Types
SimPy has several event types:
| Event | What It Does |
|---|---|
Timeout |
Fires after a duration |
Process |
Fires when process completes |
Event |
Manual trigger |
AnyOf |
Fires when any event occurs |
AllOf |
Fires when all events occur |
Timeout Events
The most common event. Wait for time to pass:
def process(env):
print(f"Start: {env.now}")
yield env.timeout(10)
print(f"After 10: {env.now}")
Zero-duration timeouts are valid:
yield env.timeout(0) # Yield but resume immediately
Useful for letting other processes run.
Process Events
A process is itself an event. Wait for another process to complete:
def worker(env):
yield env.timeout(5)
return "Done!"
def manager(env):
result = yield env.process(worker(env))
print(result) # "Done!"
You can also store the process and wait later:
proc = env.process(worker(env))
# Do other things...
yield proc
Manual Events
Create events that you trigger yourself:
def waiter(env, event):
print(f"Waiting at {env.now}")
yield event
print(f"Triggered at {env.now}")
def trigger(env, event):
yield env.timeout(5)
event.succeed() # Trigger the event
env = simpy.Environment()
my_event = env.event()
env.process(waiter(env, my_event))
env.process(trigger(env, my_event))
env.run()
Events can also fail:
event.fail(Exception("Something went wrong"))
AnyOf: Wait for Any
Wait until any event in a list occurs:
def process(env):
e1 = env.timeout(5)
e2 = env.timeout(10)
result = yield e1 | e2 # Wait for either
print(f"First event at {env.now}") # Time 5
The | operator creates an AnyOf event.
Check which event(s) occurred:
result = yield e1 | e2
if e1 in result:
print("e1 happened")
AllOf: Wait for All
Wait until all events occur:
def process(env):
e1 = env.timeout(5)
e2 = env.timeout(10)
result = yield e1 & e2 # Wait for both
print(f"Both done at {env.now}") # Time 10
The & operator creates an AllOf event.
Conditional Waits
Combine AnyOf with timeouts for deadlines:
def customer(env, resource):
req = resource.request()
patience = env.timeout(10)
result = yield req | patience
if req in result:
print("Got the resource!")
yield env.timeout(5) # Use it
resource.release(req)
else:
print("Gave up waiting")
req.cancel() # Important: cancel unused request
This is how you model customers who leave if the queue is too long.
Event Values
Events can carry values:
event = env.event()
event.succeed(value="Hello!")
def process(env, event):
result = yield event
print(result) # "Hello!"
Timeouts have None as their value by default.
Chaining Events
Events can trigger other events:
def on_complete(event):
print("Callback triggered!")
event = env.timeout(5)
event.callbacks.append(on_complete)
But usually, you just yield multiple events sequentially.
Creating Custom Events
For advanced use, subclass Event:
class MyEvent(simpy.Event):
def __init__(self, env, data):
super().__init__(env)
self.data = data
Most simulations don't need this. The built-in events handle common cases.
Event States
Events have states:
- Pending - Not yet triggered
- Triggered - Will fire at current time
- Processed - Has fired and been handled
Check state:
if event.triggered:
print("Event has occurred")
if event.processed:
print("Event has been processed")
Common Patterns
Timeout with Cleanup
def process(env, resource):
req = resource.request()
timeout = env.timeout(10)
result = yield req | timeout
if req not in result:
req.cancel() # Clean up!
Multiple Parallel Waits
def process(env):
events = [env.timeout(random.randint(1, 10)) for _ in range(5)]
while events:
result = yield simpy.AnyOf(env, events)
for event in result:
events.remove(event)
print(f"Event completed at {env.now}")
Signalling Between Processes
done_flag = env.event()
def worker(env):
yield env.timeout(10)
done_flag.succeed()
def monitor(env):
yield done_flag
print("Worker finished!")
Gotchas
Reusing Events
Events can only fire once. Create new ones for repeated use:
# Wrong
event = env.event()
yield event
yield event # Error!
# Right
event1 = env.event()
yield event1
event2 = env.event()
yield event2
Forgetting to Cancel
When using AnyOf with requests, cancel unused requests:
result = yield req | timeout
if req not in result:
req.cancel() # Don't forget!
Summary
Events are:
- What processes wait for
- Created by timeouts, processes, resources
- Combinable with | (any) and & (all)
- Triggerable manually with succeed() or fail()
Master events. Master SimPy.
Next Steps
Discover the Power of Simulation
Want to become a go-to expert in simulation with Python? The Complete Simulation Bootcamp will show you how simulation can transform your career and your projects.
Explore the Bootcamp