R simmer资源容量归0时未丢弃到达实体的问题排查与修正
问题描述
仿真场景中,部分资源采用随时段交替切换0/1容量的调度策略:
- 若资源在容量降至0前无法完成任务,到达实体需被丢弃,短暂等待后回滚至「选择可用资源」环节;
- 若无可用资源,则进入「超时→回滚」循环直至有资源可用。
原实现代码如下:
# Use a reject trajectory used to go back and select a new resource if the # current resource runs out of time (i.e., its shift ends) ... |> simmer::seize_selected( continue = TRUE, reject = simmer::trajectory('dropped') |> simmer::timeout(() runif(1, min = 0.07, max = 0.1)) |> simmer::rollback(target = 'select_resource') ) |> # Clear the queue of the selected resource when its capacity is zero simmer::renege_if( signal = 'clear_queue' ) |> simmer::send( signals = () { cap <- simmer::get_capacity_selected(env) res_name <- simmer::get_selected(env) now <- simmer::now(env) queue_count <- simmer::get_queue_count_selected(env) if (res_name == 'Operator_Bill' & now > 41 & queue_count > 0) { browser() } if (cap == 0) { 'clear_queue' } else { '' } } ) |> ...
当前问题:
- 调试条件从未触发;
- 查看
mon_resources发现,资源容量为0时仍有实体处于服务或排队状态。
问题分析
- 信号监听与发送顺序错误:原代码中
send在renege_if之后执行,实体还未注册clear_queue信号监听就收到了信号,导致信号无法被捕获。 - 信号触发时机单一:
send仅在实体进入流程时执行一次,无法响应后续资源容量的动态变化,资源容量切换时无法通知已排队/服务的实体。 - 抢占逻辑不符合需求:
seize_selected(continue = TRUE)会让抢占失败的实体继续执行后续流程,违背「丢弃后回滚」的设计。 - 调试逻辑触发条件不足:实体在资源容量变为0前已进入服务/排队,且信号未正确触发,导致
queue_count的判断逻辑从未满足。
修正后的代码
1. 资源容量调度与全局信号触发
在资源容量切换时主动发送信号,替代实体流程中的单次检查:
# 示例:定时切换Operator_Bill的容量,切换时触发信号 env |> simmer::schedule_capacity( resource = "Operator_Bill", schedule = data.frame( start_time = c(0, 40), end_time = c(40, Inf), capacity = c(1, 0) ), # 容量切换时的回调动作 action = function(env, resource) { current_cap <- simmer::get_capacity(env, resource) now <- simmer::now(env) queue_count <- simmer::get_queue_count(env, resource) # 调试逻辑:资源容量为0、时间超41且有排队实体时触发 if (resource == "Operator_Bill" && now > 41 && queue_count > 0 && current_cap == 0) { browser() } # 容量变为0时发送清队列信号 if (current_cap == 0) { simmer::send_signal(env, "clear_queue") } } )
2. 实体流程轨迹修正
调整抢占、信号监听的顺序与逻辑,确保流程符合设计要求:
simmer::trajectory("entity_flow") |> # 标记选择资源的节点,用于回滚 simmer::mark("select_resource") |> # 选择可用资源(需提前定义select逻辑) simmer::select(...) |> # 抢占选中资源:抢占失败直接进入回滚流程 simmer::seize_selected( continue = FALSE, # 抢占失败不执行后续服务逻辑 reject = simmer::trajectory("dropped") |> simmer::timeout(() runif(1, 0.07, 0.1)) |> simmer::rollback(target = "select_resource") ) |> # 注册监听clear_queue信号,触发后释放资源并回滚 simmer::renege_if( signal = "clear_queue", out = simmer::trajectory("queue_cleared") |> simmer::release_selected() |> # 释放已抢占的资源,避免占用 simmer::timeout(() runif(1, 0.07, 0.1)) |> simmer::rollback(target = "select_resource") ) |> # 执行任务流程 simmer::timeout(() rnorm(1, mean = 5)) |> simmer::release_selected()
关键说明
- 信号触发时机优化:通过
schedule_capacity的action回调,在资源容量切换时主动发送信号,确保所有监听的实体能及时响应。 - 抢占逻辑修正:
continue = FALSE让抢占失败的实体直接进入回滚流程,避免无效的后续执行。 - 回滚流程完整性:
renege_if的out轨迹中添加release_selected(),防止实体占用已切换为0容量的资源。 - 调试逻辑调整:将调试检查移到容量切换的回调中,确保满足条件时触发调试。
内容的提问来源于stack exchange,提问作者Aorus
相关产品推荐
相关产品推荐

