基于Simpy的Python仿真按需创建事件 解决发货超单、流程触发失效问题
核心问题根因定位
- 第一版代码的运行偏差来自流程顺序错误:5个提前启动的发货进程,把“获取待处理订单”的操作放在了整个发货流程的最后端。流程执行顺序是先打发货日志、算发货参数、模拟运输过程、打配送完成日志,最后才去订单容器里取1个单位的订单。这就导致所有进程哪怕还没拿到有效订单,也会完整跑一遍全流程的日志输出和统计逻辑,最终呈现的发货总量远高于实际到达的订单量。
- 第二版调整代码失效来自判断时机错误:仅在仿真启动的第0秒执行一次订单数判断,只要启动时刻没有待处理订单,就直接跳过发货进程创建。后续到货流程持续产生的新订单,永远不会触发新的发货流程启动,最终整个发货环节完全停转。
Simpy按需创建事件的核心实现逻辑
依托Simpy内置的事件阻塞特性,就能零冗余实现“订单随到随发、无积压”的目标,不需要手动维护脱离容器状态的计数变量:
simpy.Container的get操作本身是自动阻塞事件:执行yield 容器.get(需求数量)时,如果当前容器库存不足,整个进程会自动挂起,直到有新的put操作补充足够库存,才会自动解除阻塞继续运行。这个特性天然适配“有对应待处理订单才触发发货、没订单就等待”的规则。- 按需创建进程的触发规则:每次新订单完成入库(执行完
orders.put(1)操作)时,自动对比当前待处理订单总量、当前处于等待取单状态的发货进程数,差值大于0时直接新建对应数量的发货进程,保证发货处理能力和待处理订单量完全匹配,无积压、无空跑。
修正后的参考实现代码
class LC: def __init__(self, env): # 原有统计容器保留,去掉冗余的手动计数变量 self.dispatch = simpy.Container(env, capacity=100000, init=0) self.costs = simpy.Container(env, capacity=100000, init=0) self.shipment_count = simpy.Container(env, capacity=10000, init=0) self.orders = simpy.Container(env, capacity=10000, init=0) # 记录当前处于等待取单状态的发货进程数,用于按需创建时的数量匹配 self.waiting_ship_proc = 0 def shipping(env, ship): # 发货进程第一行就阻塞等待待处理订单,没拿到订单就一直挂起 yield ship.orders.get(1) ship.waiting_ship_proc -=1 print('Shipment was sent at %d' % (env.now)) # 拿到订单之后再计算发货参数 time, weight = get_weight_n_time() if weight <=35: cost = get_cost(weight,0) else: cost = get_cost(weight,1) yield env.timeout(time) print ('Shipment of %d kgs where delivered after %d days with cost of $ %d' %(weight, time, cost)) # 完成发货后更新统计值 yield ship.dispatch.put(weight) yield ship.costs.put(cost) yield ship.shipment_count.put(1) def arrival(env, ship): while True: print('Order arrived at %d'%(env.now)) # 新订单入库 yield ship.orders.put(1) # 按需创建发货进程:如果待处理订单数大于当前等待取单的发货进程数,就补建进程 pending_orders = ship.orders.level need_proc = pending_orders - ship.waiting_ship_proc for _ in range(need_proc): env.process(shipping(env, ship)) ship.waiting_ship_proc +=1 # 按照原有节奏,每1天到1个新订单 yield env.timeout(1) env = simpy.Environment() ship = LC(env) env.process(arrival(env, ship)) env.run(until=100)
关键实现注意事项
- 必须把“获取待处理订单”的代码放在发货流程的最开头,严禁先执行发货日志打印、参数计算、运输模拟等逻辑再取订单,否则会出现无单空跑的问题。
- 优先使用
simpy.Container自带的.level属性查询实时库存,不要手动维护和容器状态不同步的计数器,避免因事件调度时序偏差导致的计数不准问题。 - 如果需要保留固定5辆货车的运力配置,只需要在仿真启动时一次性创建5个发货进程,每个进程启动后第一时间执行取订单的阻塞操作,就不会出现空跑发货的问题,不需要每次判断库存再新建进程。
内容的提问来源于stack exchange,提问作者nesquess
相关产品推荐
相关产品推荐

