SimPy Resource Explained: Modelling Limited Capacity

Resources are where the interesting stuff happens. Queues form. Waiting occurs. Bottlenecks emerge.

What Is a Resource?

A Resource models something with limited capacity: tills, staff, machines, beds, vehicles.

import simpy

env = simpy.Environment()
checkout = simpy.Resource(env, capacity=2)  # 2 tills

When capacity is full, processes wait in a queue.

Basic Usage

def customer(env, name, checkout):
    print(f"{name} arrives at {env.now}")

    with checkout.request() as req:
        yield req  # Wait for a till
        print(f"{name} at till at {env.now}")
        yield env.timeout(5)  # Shopping takes 5 units

    print(f"{name} leaves at {env.now}")

env = simpy.Environment()
checkout = simpy.Resource(env, capacity=1)
env.process(customer(env, "Alice", checkout))
env.process(customer(env, "Bob", checkout))
env.run()

Alice gets the till immediately. Bob waits.

Request and Release

The with statement handles acquire and release:

with resource.request() as req:
    yield req        # Acquire
    # Use resource
# Automatically released here

Without with, manage manually:

req = resource.request()
yield req
# Use resource
resource.release(req)  # Don't forget!

The with statement is safer—guaranteed release even if exceptions occur.

Checking Resource State

print(resource.count)       # Currently in use
print(resource.capacity)    # Maximum capacity
print(len(resource.queue))  # Waiting in queue

Use during simulation:

def monitor(env, resource):
    while True:
        print(f"Time {env.now}: {resource.count}/{resource.capacity} in use, {len(resource.queue)} waiting")
        yield env.timeout(1)

Capacity of 1

The most common case—one server, one machine, one vehicle:

server = simpy.Resource(env, capacity=1)

Only one process can use it at a time. Classic queue behaviour.

Multiple Capacity

Model parallel servers:

# Bank with 3 tellers
tellers = simpy.Resource(env, capacity=3)

# Hospital with 50 beds
beds = simpy.Resource(env, capacity=50)

Multiple requests can be served simultaneously, up to capacity.

Queue Behaviour

Requests queue in FIFO order by default:

  1. Alice requests → Gets resource (first arrival)
  2. Bob requests → Waits (queue position 1)
  3. Charlie requests → Waits (queue position 2)
  4. Alice releases → Bob gets resource
  5. Bob releases → Charlie gets resource

The queue is fair. First come, first served.

Balking: Refusing to Wait

Customers might leave if the queue is too long:

def customer(env, name, resource, max_queue):
    if len(resource.queue) >= max_queue:
        print(f"{name} sees long queue, leaves")
        return

    with resource.request() as req:
        yield req
        yield env.timeout(5)

Check queue length before joining.

Reneging: Giving Up While Waiting

Customers might leave after waiting too long:

def customer(env, name, resource, patience):
    with resource.request() as req:
        result = yield req | env.timeout(patience)

        if req in result:
            # Got the resource
            yield env.timeout(5)
        else:
            # Patience ran out
            print(f"{name} gave up after waiting")

Combine request with timeout using | (AnyOf).

Collecting Statistics

Track wait times and utilisation:

wait_times = []

def customer(env, resource):
    arrival = env.now
    with resource.request() as req:
        yield req
        wait_times.append(env.now - arrival)
        yield env.timeout(5)

# After simulation
print(f"Average wait: {sum(wait_times)/len(wait_times):.2f}")

Track utilisation:

usage_log = []

def monitor(env, resource):
    while True:
        usage_log.append({
            'time': env.now,
            'utilisation': resource.count / resource.capacity
        })
        yield env.timeout(1)

Resource Types Comparison

SimPy has three resource types:

Type Use Case
Resource Countable capacity (staff, machines)
Container Continuous quantity (fuel, inventory)
Store Distinct items (parts, messages)

Resource is for "how many slots available."

Common Patterns

Multiple Resource Types

def patient(env, doctor, bed):
    # First need a doctor
    with doctor.request() as doc_req:
        yield doc_req
        yield env.timeout(10)  # Consultation

    # Then need a bed
    with bed.request() as bed_req:
        yield bed_req
        yield env.timeout(60)  # Treatment

Pool of Identical Resources

machines = simpy.Resource(env, capacity=5)

def job(env, machines):
    with machines.request() as req:
        yield req
        yield env.timeout(10)

Any of the 5 machines will do.

Tracking Which Slot

If you need to know which specific resource:

class SpecificResource:
    def __init__(self, env, names):
        self.resources = {name: simpy.Resource(env, 1) for name in names}

Or use a Store with named items.

Gotchas

Forgetting to Yield the Request

# Wrong!
with resource.request() as req:
    # req is not yielded - process doesn't wait!
    yield env.timeout(5)

You must yield req to actually wait.

Not Releasing

Without with, you must release manually:

req = resource.request()
yield req
# If you return or raise here, resource never released!
resource.release(req)

Use with to avoid this.

Infinite Capacity Trap

resource = simpy.Resource(env)  # Default capacity = inf

Without specifying capacity, it's infinite—no queuing ever happens.

Summary

Resources: - Model limited capacity (servers, machines, beds) - Queue processes when full (FIFO by default) - Use with resource.request() as req: yield req - Track .count, .capacity, .queue

Master resources. Model any queue.

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