School of Simulation Logo - Navigate to Homepage

Trustpilot Reviews

← All articles

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:

TypeUse Case
ResourceCountable capacity (staff, machines)
ContainerContinuous quantity (fuel, inventory)
StoreDistinct 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