SimPy Processes Explained: The Building Blocks of Simulation
If the environment is the heart of SimPy, processes are the muscles. They do the actual work.
What Is a Process?
A process is a Python generator function that describes behaviour over time.
def customer(env):
print(f"Customer arrives at {env.now}")
yield env.timeout(5)
print(f"Customer leaves at {env.now}")
That's it. A function. Some yields. Simulation.
The Anatomy of a Process
Every process follows the same pattern:
- Receives the environment - To access time and create events
- Performs actions - Print, calculate, interact with resources
- Yields events - Pause execution until something happens
- Resumes - When the event occurs
def process(env):
# Action
print("Starting")
# Yield event (pause)
yield env.timeout(10)
# Resume after event
print("Continuing")
Registering Processes
A process doesn't run automatically. You register it with the environment:
env = simpy.Environment()
env.process(customer(env))
env.run()
env.process() returns a Process object. You can store it:
proc = env.process(customer(env))
Multiple Processes
Real simulations have many processes running concurrently:
env = simpy.Environment()
env.process(customer(env, "Alice"))
env.process(customer(env, "Bob"))
env.process(customer(env, "Charlie"))
env.run()
All three start at time 0. SimPy interleaves their execution based on when events occur.
Process Parameters
Processes are just functions. Pass whatever parameters you need:
def customer(env, name, shopping_time, spending_limit):
print(f"{name} arrives at {env.now}")
yield env.timeout(shopping_time)
print(f"{name} spent up to £{spending_limit}")
env.process(customer(env, "Alice", 15, 50))
Processes Creating Processes
Processes can spawn other processes:
def customer_generator(env):
i = 0
while True:
yield env.timeout(5) # New customer every 5 units
env.process(customer(env, f"Customer {i}"))
i += 1
env = simpy.Environment()
env.process(customer_generator(env))
env.run(until=30)
A generator process creates customers. Customers are themselves processes. Processes creating processes. That's how you model arrivals.
Process Lifecycle
A process goes through states:
- Created - Function defined, not yet running
- Scheduled - Registered with
env.process() - Active - Currently executing (between yields)
- Waiting - Yielded an event, waiting for it to occur
- Completed - Function returned (or raised exception)
Returning Values from Processes
Processes can return values:
def calculate(env):
yield env.timeout(5)
return 42
def main(env):
result = yield env.process(calculate(env))
print(f"Got result: {result}")
env = simpy.Environment()
env.process(main(env))
env.run()
Notice: you yield the process to get its return value.
Interrupting Processes
Processes can be interrupted:
def worker(env):
try:
print("Starting work")
yield env.timeout(100)
print("Work complete")
except simpy.Interrupt as interrupt:
print(f"Interrupted: {interrupt.cause}")
def manager(env, worker_proc):
yield env.timeout(10)
worker_proc.interrupt("Tea break!")
env = simpy.Environment()
worker_proc = env.process(worker(env))
env.process(manager(env, worker_proc))
env.run()
Output:
Starting work
Interrupted: Tea break!
Process Classes
For complex processes, use classes:
class Machine:
def __init__(self, env, name):
self.env = env
self.name = name
self.parts_made = 0
self.process = env.process(self.run())
def run(self):
while True:
yield self.env.timeout(5)
self.parts_made += 1
print(f"{self.name} made part #{self.parts_made}")
env = simpy.Environment()
machine = Machine(env, "Lathe")
env.run(until=25)
print(f"Total parts: {machine.parts_made}")
The class holds state. The run method is the process. Clean separation.
Common Patterns
Infinite Loop
For ongoing processes (generators, machines):
def machine(env):
while True:
yield env.timeout(process_time)
# Do work
Finite Loop
For bounded processes:
def customer(env, tasks):
for task in tasks:
yield env.timeout(task.duration)
Conditional Exit
def worker(env):
while env.now < 500: # Work until time 500
yield env.timeout(10)
Process Communication
Processes can share data via:
- Shared variables - Be careful with timing
- Resources - Synchronisation point
- Stores - Message passing
- Events - Signalling
Gotchas
Missing yield
def broken(env):
env.timeout(5) # Missing yield!
print("This prints immediately, not after 5 units")
Without yield, the timeout is created but not waited on.
Returning vs Yielding
def wrong(env):
return env.timeout(5) # This doesn't work as expected
Use yield to pause. Use return only at the end.
Summary
Processes are:
- Generator functions with yield
- Registered with env.process()
- Can create other processes
- Can be interrupted
- Can return values
Understand processes, and you understand 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