先看题:数值输入 Numeric Value In 初值为 0,两个 For 循环各执行 5 次,循环里都用局部变量读出这个值、加上循环计数 i、再写回去。执行完,Numeric Value Out 是多少?
题目:两个循环、一个局部变量,输出是多少?
选项是 4、12、13、Undefined。正确答案是最后一个:不确定。这不是出题人故意刁难,而是这道题真正要考的东西------数据流。
一、局部变量不是一根线
连线(wire)表达的是数据依赖:上游算完,下游才能算。局部变量表达的是「同一块内存的另一个入口」,它和连线没有关系。
所以在框图上看,这两个循环之间没有任何连线。LabVIEW 看到的是两个互不依赖的结构,它可以把它们并行调度------甚至交错执行。
二、两个循环会怎么跑
没有依赖,就没有顺序保证。第一个循环完全可能先跑完,也可能跑到第 3 次时被第二个循环插进来;在多核机器上,两者还可能真的同时跑。
每读一次局部变量,拿到的是「那一刻」内存里的值;每写一次,就把新值放回去。读写之间没有互斥,也没有先后约定,这就是竞态条件(race condition)。
三、4、12、13 是怎么「碰巧」出来的
如果两个循环严格串行、且第二个循环在第一个全部写完之后才开始读,结果是一个固定的数;但只要有交错,结果就会变。
比如第二个循环的某次迭代,读到的是第一个循环刚写进去的中间值,它加出来的结果就和「等到最后再读」不一样。不同的交错顺序,能凑出 4、12、13,也能凑出别的数。
这些数字不是「答案之一」,而是「某些执行顺序下的产物」。既然题目问的是「执行完是多少」,而执行顺序不确定,答案只能是不确定。
局部变量让程序「能跑」,但跑出什么,取决于调度器当天的心情。
四、正确写法:把依赖画出来
第一种,用连线。把第一个循环的结果从隧道引出来,接到第二个循环的输入端。这样 LabVIEW 就知道必须先跑完第一个,值也只有一个来源。
第二种,用移位寄存器。如果两个循环本来就在同一个结构里,用移位寄存器把值带过去,顺序和值都明确。
第三种,用队列。如果两个循环必须并行(一个采集、一个处理),那就用队列传数据,用显式的通信代替隐式的共享内存。
三种写法的共同点:把「谁先谁后、值从哪来」变成框图上看得到的东西,而不是藏在局部变量的读写时机里。
五、局部变量什么时候可以用
不是完全不能用。同一个循环内部、单线程、只读一次做快照、或者初始化界面控件,这些场景问题不大。
但只要出现「多个循环」「读写混合」「值会变」这三个条件里的两个,就要警惕:你已经在依赖调度顺序了。
最后
这道题的价值不在答案,而在它逼你回答一个问题:你的程序,是靠数据流在跑,还是靠运气在跑?
CLAD 考的就是这个。会连线的人很多,能让程序「不靠运气」的人不多。
题目来自 LabVIEW 认证练习题(CLAD),答案为 D:Undefined。本文补充了数据流、竞态与三种正确写法的说明。
标签:LabVIEW, 局部变量, 数据流, 竞态条件, CLAD