ExeModule()方法逐段解析
整体角色
这个模块叫“数据出队”,它的核心职责是:从一个全局共享队列中取出数据,暴露为本模块的输出参数,供下游模块使用。
1. 入口计时
Stopwatch.Restart();重启一个秒表,用于性能监控/调试,记录本次执行耗时。
2. 防御性校验
if(string.IsNullOrEmpty(QueueKey)){...returnfalse;}if(!Solution.Ins.QueueDic.ContainsKey(QueueKey)){...returnfalse;}QueueKey是队列的名称标识(默认"QueueDefault"),不能为空。Solution.Ins.QueueDic是一个全局字典,存着整个解决方案里所有的队列实例。这里检查这个 key 对应的队列是否存在。
3. 阻塞等待模式(IsWait)
if(IsWait){varsignal=Solution.Ins.QueueSignDic[QueueKey];// 取出这个队列对应的"信号量"boolhasData=false;lock(outQueue){// 遍历所有启用的槽位,看有没有数据foreach(varslotinQueueSlots.Where(s=>s.IsEnable))if(GetSlotCount(outQueue,slot)>0){hasData=true;break;}}if(!hasData)signal.WaitOne(TimeOut);// 阻塞等待,直到有人"发信号"或超时}这里的关键概念:
QueueSignDic是一个Dictionary<string, AutoResetEvent>,每个队列配一个AutoResetEvent(一种线程同步信号)。- 当
IsWait = true时,模块不会立即失败,而是先检查队列里有没有数据。 - 如果所有槽位都为空,就调用
signal.WaitOne(TimeOut)— 当前线程阻塞在这里,直到:- 另一个模块(比如 DataIn 入队模块)往队列里放数据后调用了
signal.Set(),或者 - 超时时间
TimeOut(默认5000毫秒)到了。
- 另一个模块(比如 DataIn 入队模块)往队列里放数据后调用了
这实现了一个生产者-消费者模式:入队模块生产数据并 Set 信号,出队模块 WaitOne 等待消费。
4. 从每个槽位取数据
lock(outQueue){foreach(varslotinQueueSlots.Where(s=>s.IsEnable)){intcount=GetSlotCount(outQueue,slot);if(count==0){if(!IsIgnoreError)// 不忽略错误 → 报错返回{...returnfalse;}continue;// 忽略错误 → 跳过这个槽位}slot.LastValue=GetSlotValue(outQueue,slot);// 取最后一个值}}QueueSlots是一个槽位列表,每个槽位有:索引(SlotIndex)、名称(SlotName)、数据类型(DataType)。GetSlotCount:根据槽位索引和数据类型,拿到内部List<T>的元素个数。IsIgnoreError控制行为:true(默认):某个槽位没数据就跳过,继续处理下一个。false:某个槽位没数据就直接报 NG,整个模块执行失败。
GetSlotValue:取队列最后一条数据,赋值给slot.LastValue。如果IsDeleteData = true,取数据的同时还会把它从队列中移除(消费掉)。
5. 输出参数 & 状态
AddOutputParams();ChangeModuleRunStatus(eRunStatus.OK);returntrue;AddOutputParams()把每个槽位的LastValue注册为模块的输出参数(名称用SlotName,类型用DataType),这样下游模块就能通过连线拿到这些值。
一句话总结
这个方法就是一个带超时等待的队列消费者:它从全局共享队列的各个槽位里取出最新数据,转成模块的输出参数,并支持"有数据才继续 / 没数据就等"的同步模式。