SimPy中Container资源level属性瞬态值异常及日志记录咨询
SimPy Container level属性更新时机与正确日志生成方法
我正在使用SimPy进行实验,尝试监控Container资源的level属性。为便于说明,现举一个简单示例:生产者进程每3个时间步生产一个物品并放入容器,消费者进程每个时间步从容器中取出一个物品,容器初始level为3。
实现代码
import simpy # define a "producer" that puts a new item in the container every 3 time units def producer(env, container): while(True): yield env.timeout(3) print(f'Producer at t = {env.now}: Produced item. ' f'Requesting to put it into container. ' f'Current container level (before putting): {container.level}') yield container.put(1) print(f'Producer at t = {env.now}: Successfully put produced item into container. ' f'New container level: {container.level}') # define a "consumer" that attempts to take one item from the container at every time unit def consumer(env, container): while(True): print(f'Consumer at t = {env.now}: Requesting item from container. ' f'Current container level (before taking item): {container.level}') yield container.get(1) print(f'Consumer at t = {env.now}: Consumed item from container. ' f'New container level: {container.level}') yield env.timeout(2) # setup the simpy environment env = simpy.Environment() container = simpy.Container(env, init = 3) producer_process = env.process(producer(env, container)) consumer_process = env.process(consumer(env, container)) # run the simulation print(f'Initial container level: {container.level}') env.run(until=7)
运行输出
Initial container level: 3 Consumer at t = 0: Requesting item from container. Current container level (before taking item): 3 Consumer at t = 0: Consumed item from container. New container level: 2 Consumer at t = 2: Requesting item from container. Current container level (before taking item): 2 Consumer at t = 2: Consumed item from container. New container level: 1 Producer at t = 3: Produced item. Requesting to put it into container. Current container level (before putting): 1 Producer at t = 3: Successfully put produced item into container. New container level: 2 Consumer at t = 4: Requesting item from container. Current container level (before taking item): 2 Consumer at t = 4: Consumed item from container. New container level: 1 Producer at t = 6: Produced item. Requesting to put it into container. Current container level (before putting): 1 Consumer at t = 6: Requesting item from container. Current container level (before taking item): 2 Producer at t = 6: Successfully put produced item into container. New container level: 1 Consumer at t = 6: Consumed item from container. New container level: 1
我发现结果存在异常,例如在时间步6时,容器level在新生产的物品放入前就从1变为2。现咨询:SimPy中Container资源的level属性是何时及如何更新的?如何获取或生成正确的瞬态level变化日志?
一、Container level属性的更新时机与机制
SimPy的Container是事件驱动的资源,其level属性的更新遵循以下规则:
- 即时更新:当
put()或get()操作能立即满足条件(如put()时容器有剩余容量、get()时容器有足够物品),level会在操作执行瞬间同步更新,无需等待事件挂起。 - 同时间点事件的顺序影响:多个事件在同一时间点触发时,SimPy会按事件的注册顺序依次处理。若前一个事件修改了
level,后续同时间点的事件读取到的就是更新后的值——这正是t=6时异常的原因:生产者的put(1)事件比消费者的get(1)事件先被注册,SimPy先执行put操作将level从1改为2,导致消费者打印前就读取到了更新后的level。
二、生成正确瞬态level变化日志的方法
方案1:自定义LoggedContainer子类
通过继承simipy.Container,重载put()和get()方法,在操作前后强制记录level,确保日志与状态变化严格同步:
import simpy class LoggedContainer(simpy.Container): def put(self, amount): print(f'[LOG] t={self._env.now} | PUT before: level={self.level}, adding={amount}') result = super().put(amount) def log_after_put(event): print(f'[LOG] t={self._env.now} | PUT after: level={self.level}') result.callbacks.append(log_after_put) return result def get(self, amount): print(f'[LOG] t={self._env.now} | GET before: level={self.level}, taking={amount}') result = super().get(amount) def log_after_get(event): print(f'[LOG] t={self._env.now} | GET after: level={self.level}') result.callbacks.append(log_after_get) return result # 原生产者/消费者逻辑不变,替换容器类 def producer(env, container): while(True): yield env.timeout(3) yield container.put(1) def consumer(env, container): while(True): yield container.get(1) yield env.timeout(2) env = simpy.Environment() container = LoggedContainer(env, init=3) env.process(producer(env, container)) env.process(consumer(env, container)) env.run(until=7)
方案2:利用事件回调机制
不修改容器类,在创建put()/get()事件时添加回调函数,在事件完成时记录level:
import simpy def log_put_success(env, container, amount): print(f'[LOG] t={env.now} | PUT completed: level={container.level}') def log_get_success(env, container, amount): print(f'[LOG] t={env.now} | GET completed: level={container.level}') def producer(env, container): while(True): yield env.timeout(3) put_event = container.put(1) put_event.callbacks.append(lambda e: log_put_success(env, container, 1)) yield put_event def consumer(env, container): while(True): get_event = container.get(1) get_event.callbacks.append(lambda e: log_get_success(env, container, 1)) yield get_event yield env.timeout(2) env = simpy.Environment() container = simpy.Container(env, init=3) print(f'Initial container level: {container.level}') env.process(producer(env, container)) env.process(consumer(env, container)) env.run(until=7)
三、t=6异常的具体解释
原代码中t=6的事件执行顺序为:
- 生产者的
timeout(3)触发,打印level=1,发起put(1)请求; - 消费者的
timeout(2)触发,此时SimPy已处理完生产者的put操作,level变为2,消费者打印该值后发起get(1)请求; - 生产者的
put操作完成,此时get操作已同步执行,level被改回1,生产者打印该值; - 消费者的
get操作完成,打印level=1。
SimPy按事件创建顺序处理同时间点事件,导致消费者读取到了提前更新的level值。
内容的提问来源于stack exchange,提问作者kaffeesachse
相关产品推荐
相关产品推荐

