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:

  1. Receives the environment - To access time and create events
  2. Performs actions - Print, calculate, interact with resources
  3. Yields events - Pause execution until something happens
  4. 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:

  1. Created - Function defined, not yet running
  2. Scheduled - Registered with env.process()
  3. Active - Currently executing (between yields)
  4. Waiting - Yielded an event, waiting for it to occur
  5. 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:

  1. Shared variables - Be careful with timing
  2. Resources - Synchronisation point
  3. Stores - Message passing
  4. 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