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:
- Alice requests → Gets resource (first arrival)
- Bob requests → Waits (queue position 1)
- Charlie requests → Waits (queue position 2)
- Alice releases → Bob gets resource
- 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