传引用
传值是符合数据流驱动程序的传参方式,在 LabVIEW 中应该尽量使用这种方式。但是传引用在某些情况下是不可避免的。假如程序要在不同的线程中对同一数据进行操作,就不得不用到传引用。
在 C++ 中,引用是用一个 4 字节(在 32 位操作系统下)或 8 字节(在 64 位操作系统下)的数据来表示一块数据的地址,数据本身则被保存在某个可以被多个资源同时访问到的内存控件中。在 LabVIEW 中,传引用的形式更加丰富。
LabVIEW 自带的传引用数据类型
在 C、 Java 等常见文本编程语言中,一个数据传入子函数的时候,可以被指定是传值还是传引用。但 LabVIEW 不同,LabVIEW 不能指定一个数据是传值还是传引用。但是 LabVIEW 的数据类型被区分为传值型的和传引用型的。LabVIEW 中大部分数据类型是值传递的,而另有一部分数据类型专门用于传引用。
控件选板的“引用句柄”栏上的控件就都是传引用数据类型的控件:
在程序框图上,用深绿色细线表示这类传引用的数据类型。比如我们在全局变量一节介绍过的信号量的引用,就是一种典型的引用数据类型。下图程序中在各个子 VI 间,绿色细线上传递的数据就是信号量的引用:
除了控件选板上的各种“引用句柄”外,LabVIEW 还有其它一些数据类型,尽管其数据线的颜色不同,但其实也属于传引用的数据类型。其中包括了硬件设备的句柄(如 VISA 资源名称、IVI 逻辑名等)、通知、事件、队列等等。
LabVIEW 中还有相当多的引用数据类型,它们的控件并没有列在控件选板上。比如我们事件结构一节介绍了为窗格和布尔控件等对象创建引用的方法,它们的引用控件是无法在控件选板中找到的。只能通过先为某个对象创建引用,再从引用的接线端创建控件,通过这样一种间接的方法来得到这些引用类型的控件。比如下面这些引用控件:
引用数据类型本身是一个 4 字节的数据,用这个 4 字节的数据再指向一个其它的对象,程序中真正需要使用的是它所指向的对象。各种不同引用类型的区别在于它们所指向的对象种类是不同的。比如有的引用指向一个文件,有的指向仪器设备,有的指向某个 VI 或控件。
在使用了这些传引用数据类型的程序框图上,数据线上流动的是引用类型本身的 4 字节数据,而他们所指向的对象是不动的。如果引用所指向的是一大块数据,那么,在节点间传递 4 字节的效率当然比传递这一大块数据要高很多。在数据线分叉的地方,LabVIEW 只需要把引用本身的 4 字节数据生成一个副本,引用所指向的对象并不需要被复制。引用副本指向的还是原来的对象,这样,程序可以在不同的代码分支中都访问同一个对象,同一块数据。
全局变量
除了 LabVIEW 自带的传引用数据类型,能够传递特定数据的引用之外,我们也可以通过其它手段,把任意一块数据以引用的方式传递。
全局变量(和局部变量)是一种最简便的传引用的方法。全局 变量的数据实际上是被保存在某一固定的内存空间的,在不同的 VI 或线程中,都可以通过全局变量访问数据。
在使用全局变量时,直接把表示全局变量的 VI 或节点放在程序中就可以访问它的数据了。这种方式尽管有优点,但缺点更明显。我们在阅读 LabVIEW 程序的时候,数据线是非常重要的线索。它为我们指明了程序执行的顺序,数据传递和加工的过程。失去数据线这一重要线索,就不容易搞清楚某个数据是从哪里来的,何时被改动,因而大大降低了程序的可读性和可维护性。
所以,很多时候,数据线是非常必要的。在传引用时,还是希望能够有一根数据线用来传递这个引用。
队列
队列用作数据结构
我们在状态机一节中曾经简要的介绍过队列这种数据结构。队列是一种数据结构,队列中可存放多个类型相同的数据。队列的行为就像是排队买票,数据进入队列,就好像顾客进入排队,先排进去的顾客一定会被先处理,先出队伍。队列中的数据必须是先进先出,即每次从队列中取出一个数据时,只能取队列中最先被放进去的那个。
队列(Queue)在 LabVIEW 中是一种非常特殊的数据结构:其它的数据结构,比如数组(Array)、映射表(Map)、集合(Set)等都和常见的简单数据类型一样采用值传递的,而队列却是传引用的。这是因为在“队列”被添加到 LabVIEW 的时候,LabVIEW 的应用还比较局限,主要用于编写基础的测试程序。LabVIEW 处理的数据类型主要也是硬件返回的测试结果等,比较直观和单一,并不需要复杂的数据结构来表示。反而,因为 LabVIEW 是自动多线程的,用户不自觉的就会编写出多线程程序出来,它更需要不是如何优化数据的组织结构,而是一种方便高效的方法让程序在不同线程间传递数据。这一方法就是使用队列,把队列做成引用类型,以方便的在不同线程之间的数据间共享数据。
至今,队列相关的函数都还在“编程 -> 同步”函数选板上,而不是跟别的数据结构一起被放置在“数据容器”选板上。
队列一般指的就是单向队列,是简单的先进先出,数据元素只能从队列尾入队,从队列头出队。经常拿来与队列进行比较的数据结构是“栈”(在状态机一节也简要介绍过)。栈这种数据结构,有点像手枪的弹夹,数据放入栈,就好像子弹压入弹夹,先压入的子弹一定是后出来的。栈中的数据元素必须是后进先出。
还有一种双向队列,除了常规的入队出队顺序,也同时允许数据元素从队列头入队,从队列尾出队。双向队列既可以被当做队列使用,也可以被当做栈使用。LabVIEW 提供的队列是半双向的,它虽然不允许数据从队列尾出队,但是允许我们把数据从队列头入队。这样,LabVIEW 的队列其实也可以在需要的时候被当做栈来使用。