Resources count slots. Containers track levels. Stores hold distinct items.
When you need to pass actual objects between processes—parts, messages, jobs—Store is your tool.
When to Use Store
Resource: “Give me any available server.”
Container: “Give me 10 litres of fuel.”
Store: “Give me the next item in the queue.”
import simpy
env = simpy.Environment()
store = simpy.Store(env, capacity=10)
Basic Operations
# Put an item
yield store.put("item1")
# Get an item (FIFO)
item = yield store.get()
print(item) # "item1"
Items are objects. Strings, numbers, custom classes—anything.
Producer-Consumer Pattern
def producer(env, store):
for i in range(5):
item = f"Product_{i}"
yield store.put(item)
print(f"Produced {item} at {env.now}")
yield env.timeout(2)
def consumer(env, store):
while True:
item = yield store.get()
print(f"Consumed {item} at {env.now}")
yield env.timeout(3)
env = simpy.Environment()
store = simpy.Store(env)
env.process(producer(env, store))
env.process(consumer(env, store))
env.run(until=20)
Blocking Behaviour
put() blocks when store is full:
store = simpy.Store(env, capacity=3)
yield store.put("a") # OK
yield store.put("b") # OK
yield store.put("c") # OK
yield store.put("d") # Blocks until space available
get() blocks when store is empty:
store = simpy.Store(env)
item = yield store.get() # Blocks until something is put
Custom Objects
Stores shine with complex objects:
class Part:
def __init__(self, part_id, part_type, priority):
self.part_id = part_id
self.part_type = part_type
self.priority = priority
def producer(env, store):
for i in range(10):
part = Part(i, "widget", priority=i % 3)
yield store.put(part)
yield env.timeout(1)
def consumer(env, store):
while True:
part = yield store.get()
print(f"Processing part {part.part_id} ({part.part_type})")
yield env.timeout(2)
Checking Store State
store.items # List of items currently in store
store.capacity # Maximum capacity (inf if not specified)
len(store.items) # Current count
Unlimited Capacity
By default, stores have infinite capacity:
store = simpy.Store(env) # No limit
For bounded buffers, specify capacity:
store = simpy.Store(env, capacity=50)
FilterStore: Selective Getting
What if you need a specific type of item? Use FilterStore:
store = simpy.FilterStore(env, capacity=10)
# Put various items
yield store.put({'type': 'red', 'id': 1})
yield store.put({'type': 'blue', 'id': 2})
yield store.put({'type': 'red', 'id': 3})
# Get only red items
red_item = yield store.get(lambda item: item['type'] == 'red')
print(red_item) # {'type': 'red', 'id': 1}
The filter function decides which items match.
PriorityStore: Priority-Based Getting
Get items by priority:
from simpy import PriorityItem
store = simpy.PriorityStore(env, capacity=10)
yield store.put(PriorityItem(priority=3, item="low"))
yield store.put(PriorityItem(priority=1, item="high"))
yield store.put(PriorityItem(priority=2, item="medium"))
item = yield store.get() # Gets "high" (priority 1)
Lower priority number = higher priority (retrieved first).
Real-World Example: Job Queue
class Job:
def __init__(self, job_id, job_type, duration):
self.job_id = job_id
self.job_type = job_type
self.duration = duration
self.created = None
def job_creator(env, job_queue):
job_id = 0
while True:
job = Job(job_id, "standard", random.uniform(5, 15))
job.created = env.now
yield job_queue.put(job)
print(f"Job {job_id} created at {env.now}")
job_id += 1
yield env.timeout(random.expovariate(1/3))
def worker(env, name, job_queue):
while True:
job = yield job_queue.get()
wait_time = env.now - job.created
print(f"{name} starts job {job.job_id} (waited {wait_time:.1f})")
yield env.timeout(job.duration)
print(f"{name} finished job {job.job_id}")
env = simpy.Environment()
job_queue = simpy.Store(env)
env.process(job_creator(env, job_queue))
env.process(worker(env, "Worker1", job_queue))
env.process(worker(env, "Worker2", job_queue))
env.run(until=50)
Store vs Resource
| Feature | Resource | Store |
|---|---|---|
| What it holds | Slots | Objects |
| Put operation | request() | put(item) |
| Get operation | N/A (release) | get() |
| Items returned | No | Yes |
| Custom items | No | Yes |
Use Resource when you just need capacity. Use Store when you need the actual items.
Store vs Container
| Feature | Container | Store |
|---|---|---|
| What it tracks | Levels/amounts | Distinct items |
| Put amount | put(quantity) | put(item) |
| Get amount | get(quantity) | get() (one item) |
| Items are | Fungible | Distinct |
Use Container for fuel, inventory levels. Use Store for parts, messages, jobs.
Common Patterns
Message Passing
mailbox = simpy.Store(env)
def sender(env, mailbox):
yield mailbox.put({'type': 'greeting', 'content': 'Hello'})
def receiver(env, mailbox):
message = yield mailbox.get()
print(f"Received: {message['content']}")
Conveyor/Buffer
buffer = simpy.Store(env, capacity=20)
def machine_a(env, buffer):
while True:
part = produce_part()
yield buffer.put(part)
yield env.timeout(production_time)
def machine_b(env, buffer):
while True:
part = yield buffer.get()
process(part)
yield env.timeout(processing_time)
Summary
Store:
- Holds distinct items, not slots or levels
- FIFO by default
- Use FilterStore for selective gets
- Use PriorityStore for priority-based gets
- Perfect for jobs, messages, parts, orders
Pass objects between processes. Use Store.

