4. 测试序列并行化设计
在单工位测试系统中,序列通常按“初始化→激励→测量→判定→清理”的线性顺序编写,每一步都依赖前一步的结果或硬件状态。当需要把同一套测试逻辑扩展到多工位并行执行时,如果直接把单工位序列复制到多个执行线程,往往会遇到资源争用、状态串扰和结果错乱三类问题。并行化设计的核心,不是简单地把序列“跑多份”,而是把序列中可独立执行的部分与必须串行协调的部分识别出来,再按工位维度重新组织执行结构。
一、先识别序列中的三类步骤
重构之前,需要对原单工位序列做一次步骤分类。判断依据是:该步骤是否访问独占硬件资源、是否依赖其他工位的状态、是否产生需要汇总的测试结果。
- 工位私有步骤:只操作本工位专属资源,例如本工位的DMM通道、本工位的电源通道、本工位的继电器矩阵列。这类步骤可以完全并行,各工位互不干扰。
- 共享资源步骤:访问全局唯一资源,例如系统级参考时钟、共享射频开关、公共负载总线。这类步骤必须加锁或排队,不能同时进入。
- 同步点步骤:需要多个工位同时到达某一状态才能继续,例如多工位同时加电、同时触发采集。这类步骤需要屏障(Barrier)或事件同步。
以某电源模块测试为例,单工位序列包含:打开本工位电源→设置输出电压→等待稳定→读取本工位DMM→切换共享负载→再次读取→关闭电源。其中“切换共享负载”属于共享资源步骤,其余均为工位私有步骤。识别出这一点后,并行化方案就清晰了:私有步骤并行执行,共享步骤串行化并加锁。
二、用“工位实例+共享资源池”重构序列结构
TestStand中实现并行的常见方式有两种:一是使用并行测试模型(Parallel Model),由模型自动为每个工位创建独立执行线程;二是使用Batch Synchronization与Lock步骤手动控制同步。无论哪种方式,序列结构都应从“一条线性链”改为“每个工位一条实例链,共享资源单独成池”。
具体重构时,把原序列拆成三部分:
- 工位初始化序列:每个工位独立执行,完成本工位资源打开、自检、参数加载。此序列不访问共享资源,可完全并行。
- 测试主体序列:包含私有测量步骤和共享资源访问步骤。私有步骤直接执行;共享步骤前插入Lock,步骤后插入Unlock,锁的粒度尽量小,只包住真正访问共享资源的那一步。
- 结果汇总序列:各工位把本工位结果写入独立的结果容器,最后由主线程或汇总步骤统一读取,避免多工位同时写同一结果变量。
下面给出一个可参考的序列结构示意,展示单工位线性序列如何映射为多工位并行结构,以及共享资源锁在其中的位置。
三、锁的粒度与同步点设计
并行化最容易出错的地方是锁的粒度。锁太大,多个工位在共享资源前排队,并行度下降;锁太小,可能漏掉对共享资源的连续访问,导致状态不一致。一个实用的原则是:锁只包住“从设置共享资源到读取该资源影响结果”的最小连续区间。例如切换共享负载后需要等待稳定再读取,那么Lock应覆盖“切换+等待+读取”三步,而不是只覆盖“切换”一步。
同步点则用于需要多工位同时动作的场景。TestStand中可用Batch Synchronization步骤实现屏障:所有工位到达该步骤后一起释放。典型用法是多工位同时加电以模拟真实负载条件。同步点不宜过多,每增加一个同步点,整体节拍就由最慢工位决定,因此只应在测试逻辑确实要求“同时”时才使用。
四、结果容器与数据隔离
并行执行时,多个工位不能写同一个结果变量。推荐做法是为每个工位分配独立的结果容器,例如使用RunState.TestSockets.MyIndex作为索引,把测量值写入StationResults[MyIndex]。汇总步骤在所有工位结束后统一读取该数组,再写入报告。这样既避免了写冲突,也保留了每个工位的原始数据,便于后续按工位追溯。
五、重构检查清单
| 检查项 | 判断标准 | 常见问题 |
|---|---|---|
| 私有步骤是否完全并行 | 各工位私有步骤无共享变量、无共享硬件 | 误把本工位资源写成全局变量 |
| 共享步骤是否加锁 | 每个共享资源访问都有对应Lock/Unlock | 漏锁导致负载状态被覆盖 |
| 锁区间是否最小 | 锁内只包含必要的连续操作 | 锁包住整个测试主体,并行度归零 |
| 同步点是否必要 | 只有逻辑要求“同时”时才使用屏障 | 同步点过多,节拍被最慢工位拖累 |
| 结果是否隔离 | 每工位独立结果容器,汇总统一读取 | 多工位写同一变量,结果错乱 |
把单工位序列重构为可并行执行的序列结构,本质上是一次“资源归属”的重新划分:先分清哪些步骤只属于本工位,哪些步骤必须全局协调,再用锁和同步点把共享部分保护起来,用独立结果容器把数据隔离开。完成这一步之后,工位数量增加才不会带来逻辑混乱,后续的节拍优化和资源调度才有稳定的基础。