SimPy 的资源共享,Resource、Container、Store 等
仿真中涉及的人力资源以及工艺上的物料消耗都会抽象用 Resource
来表达,主要的 method
是 request
。Store
处理各种优先级的队列问题,表现跟 queue
一致,通过 get()/put()
存放 item
。
1 2 3 4 5 6 BaseResource(capacity): put_queue get_queue put(): event get(): event
每个资源都有一个最大容量和两个队列:一个队列用于要向其中放入内容的进程,另一个队列则用于要取出内容的进程。put()
和 get()
方法都返回一个在相应操作成功时触发的事件。
SimPy 定义了三类资源:
Resources
:一次可由有限数量的过程使用的资源(例如,具有有限数量燃油泵的加油站)
资源或限制条件:simpy.Resource(env, capacity)
优先级、不可打断正在服务的进程:simpy.PriorityResource
优先级、可以打断正在服务的进程:simpy.PreemptiveResource
Containers
:模拟同质、未分散的生产、消费资源。它可以是连续的(如水)或离散的(如苹果)
Stores
:允许生产和使用 Python 对象的资源
存取 Item
、遵循先来后到:simpy.Store
添加优先级:simpy.PriorityStore
存在分类:simpy.FilterStore
Resources
APIs
simpy.Resource(env, capacity = )
simpy.Resource.request(priority = )
simpy.Resource.release()
Example 3.1
电动汽车使用两个充电桩中间的一个进行充电。如果这两个点都在使用,汽车将等待其中一个点直至其再次可用,然后开始给充电,完成后离开充电站
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 import simpydef car (env, name, bcs, driving_time, charge_duration ): yield env.timeout(driving_time) print ('%s 到达时间 %d' % (name, env.now)) with bcs.request() as req: yield req print ('%s 充电开始时间 %d' % (name, env.now)) yield env.timeout(charge_duration) print ('%s 充电结束并驶离时间 %d' % (name, env.now)) env = simpy.Environment() bcs = simpy.Resource(env, capacity = 2 ) for i in range (4 ): env.process(car(env, '第 %d 辆车' % (i + 1 ), bcs, i * 2 , 5 )) env.run()
资源 resource
的 request()
方法生成一个事件 event
, 该事件会等待直至资源可用。之后一直拥有资源,直至资源被释放。
在代码中使用 with
构造资源的请求使用,资源在使用后将自动释放
。如果没通过 with
使用 request()
,需要自行使用 release()
释放资源。
释放资源后,下一个等待的进程将激活,占用资源。资源的排队规则遵循先进先出 FIFO
策略。
查看结果
1 2 3 4 5 6 7 8 9 10 11 12 第 1 辆车 到达时间 0 第 1 辆车 充电开始时间 0 第 2 辆车 到达时间 2 第 2 辆车 充电开始时间 2 第 3 辆车 到达时间 4 第 1 辆车 充电结束并驶离时间 5 第 3 辆车 充电开始时间 5 第 4 辆车 到达时间 6 第 2 辆车 充电结束并驶离时间 7 第 4 辆车 充电开始时间 7 第 3 辆车 充电结束并驶离时间 10 第 4 辆车 充电结束并驶离时间 12
Example 3.2
银行排队服务例子:一个柜台对客户进行服务,服务耗时,客户等候过长会离开柜台
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 """ 银行排队服务例子:一个柜台对客户进行服务,服务耗时,客户等候过长会离开柜台 """ import simpyimport randomRANDOM_SEED = 42 NEW_CUSTOMERS = 5 INTERVAL_CUSTOMERS = 10.0 MIN_PATIENCE = 1 MAX_PATIENCE = 3 def source (env, number, interval, counter ): for i in range (number): c = customer(env, 'Customer%02d' % i, counter, time_in_bank = 12.0 ) env.process(c) t = random.expovariate(1.0 / interval) yield env.timeout(t) def customer (env, name, counter, time_in_bank ): arrive = env.now print ('%7.4f %s: Here I am' % (arrive, name)) with counter.request() as req: patience = random.uniform(MIN_PATIENCE, MAX_PATIENCE) results = yield req | env.timeout(patience) wait = env.now - arrive if req in results: print ('%7.4f %s: Waited %6.3f' % (env.now, name, wait)) tib = random.expovariate(1.0 / time_in_bank) yield env.timeout(tib) print ('%7.4f %s: Finished' % (env.now, name)) else : print ('%7.4f %s: RENEGED after %6.3f' % (env.now, name, wait)) print ('Bank renege' )random.seed(RANDOM_SEED) env = simpy.Environment() counter = simpy.Resource(env, capacity = 1 ) env.process(source(env, NEW_CUSTOMERS, INTERVAL_CUSTOMERS, counter)) env.run()
查看结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 Bank renege 0.0000 Customer00: Here I am 0.0000 Customer00: Waited 0.000 3.8595 Customer00: Finished 10.2006 Customer01: Here I am 10.2006 Customer01: Waited 0.000 12.7265 Customer02: Here I am 13.9003 Customer02: RENEGED after 1.174 23.7507 Customer01: Finished 34.9993 Customer03: Here I am 34.9993 Customer03: Waited 0.000 37.9599 Customer03: Finished 40.4798 Customer04: Here I am 40.4798 Customer04: Waited 0.000 43.1401 Customer04: Finished
Example 3.3
PriorityResource
优先资源。此资源子类允许请求进程为每个请求提供优先级。更重要的请求将比不重要的请求更早地访问资源。优先级用整数表示;较小的数字意味着较高的优先级
。
优先资源例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 import simpydef resource_user (name, env, resource, wait, prio ): print (f'{name} 在 {env.now} 到达,优先级是 {prio} ' ) print (f'{name} 挂起...' ) yield env.timeout(wait) with resource.request(priority=prio) as req: print (f'{name} requesting at {env.now} with priority={prio} ' ) yield req print (f'{name} got resource at {env.now} ' ) yield env.timeout(3 ) env = simpy.Environment() res = simpy.PriorityResource(env, capacity=1 ) p2 = env.process(resource_user(2 , env, res, wait=1 , prio=0 )) p1 = env.process(resource_user(1 , env, res, wait=0 , prio=0 )) p3 = env.process(resource_user(3 , env, res, wait=2 , prio=-1 )) env.run()
查看结果
1 2 3 4 5 6 7 8 9 10 11 12 2 在 0 到达,优先级是 0 2 挂起...1 在 0 到达,优先级是 0 1 挂起...3 在 0 到达,优先级是 -1 3 挂起...1 requesting at 0 with priority=0 1 got resource at 0 2 requesting at 1 with priority=0 3 requesting at 2 with priority=-1 3 got resource at 3 2 got resource at 6
Example 3.4
PreemptiveResource
抢占资源。
抢占资源例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 import simpydef resource_user (name, env, resource, wait, prio ): print (f'{name} 在 {env.now} 到达,优先级是 {prio} ' ) print (f'{name} 挂起...' ) yield env.timeout(wait) with resource.request(priority=prio) as req: print (f'{name} requesting at {env.now} with priority={prio} ' ) yield req print (f'{name} got resource at {env.now} ' ) try : yield env.timeout(3 ) except simpy.Interrupt as interrupt: by = interrupt.cause.by usage = env.now - interrupt.cause.usage_since print (f'{name} got preempted by {by} at {env.now} ' f' after {usage} ' ) env = simpy.Environment() res = simpy.PreemptiveResource(env, capacity=1 ) p3 = env.process(resource_user(3 , env, res, wait=2 , prio=-1 )) p1 = env.process(resource_user(1 , env, res, wait=0 , prio=0 )) p2 = env.process(resource_user(2 , env, res, wait=1 , prio=0 )) env.run()
查看结果
1 2 3 4 5 6 7 8 9 10 11 12 13 3 在 0 到达,优先级是 -1 3 挂起... 1 在 0 到达,优先级是 0 1 挂起... 2 在 0 到达,优先级是 0 2 挂起... 1 requesting at 0 with priority=0 1 got resource at 0 2 requesting at 1 with priority=0 3 requesting at 2 with priority=-1 1 got preempted by <Process(resource_user) object at 0x7fa733116898> at 2 after 2 3 got resource at 2 2 got resource at 5
Example 3.5
抢占请求不允许跳过优先级更高的请求。以下示例显示,在队列中,抢占式的低优先级请求无法插队非抢占式的高优先级请求。
抢占式低优先级不允许跳过例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 import simpydef user (name, env, res, prio, preempt ): with res.request(priority=prio, preempt=preempt) as req: try : print (f'{name} requesting at {env.now} ' ) assert isinstance (env.now, int ), type (env.now) yield req assert isinstance (env.now, int ), type (env.now) print (f'{name} got resource at {env.now} ' ) yield env.timeout(3 ) except simpy.Interrupt: print (f'{name} got preempted at {env.now} ' ) env = simpy.Environment() res = simpy.PreemptiveResource(env, capacity=1 ) A = env.process(user('A' , env, res, prio=0 , preempt=True )) env.run(until=1 ) B = env.process(user('B' , env, res, prio=-2 , preempt=False )) C = env.process(user('C' , env, res, prio=-1 , preempt=True )) env.run()
处理 A 请求优先级为 0 的资源。它立即成为用户
进程 B 请求优先级为 -2 的资源,但将 preempt 设置为 False。它会排队等候
进程 C 请求优先级为 -1 的资源,但保留 preempt 为 True。通常情况下,它会抢占 A,但在这种情况下,B 在 C 之前排队,并阻止 C 抢占 A。由于 B 的优先级不够高,C 也不能抢占 B
由于进程B的优先级较高,在本例中不发生抢占。优先级为 -3 的请求可以抢占 A。
查看结果
1 2 3 4 5 6 A requesting at 0 A got resource at 0 B requesting at 1 C requesting at 1 B got resource at 3 C got resource at 6
Containers
APIs
simpy.Container(env, init = , capacity = )
simpy.Container.level
simpy.Container.put()
simpy.Container.get()
Example 3.6
模拟加油站的 tank。Tanker 增加了 tank 中的汽油量,而 Car 则减少了汽油量
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 import simpyclass GasStation : def __init__ (self, env ): self.fuel_dispensers = simpy.Resource(env, capacity=2 ) self.gas_tank = simpy.Container(env, init=100 , capacity=1000 ) self.mon_proc = env.process(self.monitor_tank(env)) def monitor_tank (self, env ): while True : if self.gas_tank.level < 100 : print (f'Calling tanker at {env.now} ' ) env.process(tanker(env, self)) yield env.timeout(15 ) def tanker (env, gas_station ): yield env.timeout(10 ) print (f'Tanker arriving at {env.now} ' ) amount = gas_station.gas_tank.capacity - gas_station.gas_tank.level yield gas_station.gas_tank.put(amount) def car (name, env, gas_station ): print (f'Car {name} arriving at {env.now} ' ) with gas_station.fuel_dispensers.request() as req: yield req print (f'Car {name} starts refueling at {env.now} ' ) yield gas_station.gas_tank.get(40 ) yield env.timeout(5 ) print (f'Car {name} done refueling at {env.now} ' ) def car_generator (env, gas_station ): for i in range (4 ): env.process(car(i, env, gas_station)) yield env.timeout(5 ) env = simpy.Environment() gas_station = GasStation(env) car_gen = env.process(car_generator(env, gas_station)) env.run(35 )
加油站具有数量有限的加油机(建模为 Resource
)和油箱(建模为 Container
)。可以检查其当前液位及其容量(GasStation.monitor_tank()
和 tanker()
)。可以通过 put_queue
和 get_queue
属性(类似于Resource.queue
)访问等待事件列表。
查看结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 Car 0 arriving at 0 Car 0 starts refueling at 0 Car 1 arriving at 5 Car 0 done refueling at 5 Car 1 starts refueling at 5 Car 2 arriving at 10 Car 1 done refueling at 10 Car 2 starts refueling at 10 Calling tanker at 15 Car 3 arriving at 15 Car 3 starts refueling at 15 Tanker arriving at 25 Car 2 done refueling at 30 Car 3 done refueling at 30
Stores
使用 Stores
可以对具体对象的生产和消费进行建模。单个 Store
甚至可以包含多种类型的对象。
APIs
simpy.Store(env, capcity = )
simpy.Store.get()
simpy.Store.put()
simpy.PriorityStore()
simpy.PriorityItem(priority, item)
Example 3.7
一般生产者/消费者场景
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 import simpydef producer (env, store ): for i in range (100 ): yield env.timeout(2 ) yield store.put(f'spam {i} ' ) print (f'Produced spam at' , env.now) def consumer (name, env, store ): while True : yield env.timeout(1 ) print (name, 'requesting spam at' , env.now) item = yield store.get() print (name, 'got' , item, 'at' , env.now) env = simpy.Environment() store = simpy.Store(env, capacity=2 ) prod = env.process(producer(env, store)) consumers = [env.process(consumer(i, env, store)) for i in range (2 )] env.run(until=10 )
查看结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 0 requesting spam at 1 1 requesting spam at 1 Produced spam at 2 0 got spam 0 at 2 0 requesting spam at 3 Produced spam at 4 1 got spam 1 at 4 1 requesting spam at 5 Produced spam at 6 0 got spam 2 at 6 0 requesting spam at 7 Produced spam at 8 1 got spam 3 at 8 1 requesting spam at 9
Example 3.8
PriorityStore
具有优先级的 Store。
inspector process 发现并记录单独的 maintainer process 按优先级顺序修复的问题。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 import simpyenv = simpy.Environment() issues = simpy.PriorityStore(env) def inspector (env, issues ): for issue in [simpy.PriorityItem('P2' , '#0000' ), simpy.PriorityItem('P0' , '#0001' ), simpy.PriorityItem('P3' , '#0002' ), simpy.PriorityItem('P1' , '#0003' )]: yield env.timeout(1 ) print (env.now, 'log' , issue) yield issues.put(issue) def maintainer (env, issues ): while True : yield env.timeout(3 ) issue = yield issues.get() print (env.now, 'repair' , issue) _ = env.process(inspector(env, issues)) _ = env.process(maintainer(env, issues)) env.run()
查看结果
1 2 3 4 5 6 7 8 1 log PriorityItem(priority='P2' , item='#0000' ) 2 log PriorityItem(priority='P0' , item='#0001' ) 3 log PriorityItem(priority='P3' , item='#0002' ) 3 repair PriorityItem(priority='P0' , item='#0001' ) 4 log PriorityItem(priority='P1' , item='#0003' ) 6 repair PriorityItem(priority='P1' , item='#0003' ) 9 repair PriorityItem(priority='P2' , item='#0000' ) 12 repair PriorityItem(priority='P3' , item='#0002' )
SimPy 学习