LabVIEW DVR(数据值引用)深度解析:理解内存分配机制与正确用法
2026/6/16 8:24:58 网站建设 项目流程

LabVIEW DVR(数据值引用)深度解析:理解内存分配机制与正确用法

一、开篇概述

Data Value Reference(DVR,数据值引用)是 LabVIEW 中一个强大但容易被误解的功能。很多开发者在使用 DVR 时遇到的第一道坎就是:创建 DVR 指向同一个类实例,为什么每个引用却各自拥有一份独立的数据?本文从一个真实论坛案例出发,深入分析 DVR 的内存分配机制,帮助工程师从根本上理解 DVR 的行为模式。

二、一个经典问题:引用为何不引用同一份数据?

2.1 问题场景

开发者创建了一个 LabVIEW 类,其中包含一个 I32 数值和一个增加该数值的 Increment 方法。主程序可以启动多个子 VI(Child VI)窗口,每个子 VI 都是可重入的。在启动每个子 VI 时,开发者创建一个 DVR 指向这个类的实例,期望所有子 VI 都操作同一份数据——即任何一个子 VI 调用 Increment 方法,所有子 VI 看到的数值同步增长。

但实际表现是:每个子 VI 各自拥有独立的数据空间,互不影响。启动三个子 VI,就有三个不同的整数值。

2.2 问题的核心

开发者困惑的核心在于直觉上的矛盾:"A reference to X is a reference to X, isn't it?"(指向 X 的引用就是指向 X 的引用,对吗?)

答案是否定的。DVR 的 Create 操作不是创建一个指向现有数据的链接,而是在内存中分配一个新空间,并将输入数据复制进去。

三、技术原理

3.1 DVR的本质:内存位置,而非指向已有数据的指针

DVR 的本质是一个内存中存放数据的位置(a location in memory where data is held)。两个 DVR 不能指向同一块内存位置,它们只能指向相同的数据类型。

当在程序框图上调用 New Data Value Reference 时,实际上发生了两件事:第一,在堆上分配一块新的独立内存区域;第二,将当前输入到该函数的数据复制到这片新内存中。这个过程在行为上类似于 Obtain Queue——每次调用创建一个新队列,即便每次都传入同一个队列常量,每次调用仍然产生一个独立的队列。

3.2 分裂数据线的类比

正如论坛中的回复所指出的:What ever you wire into a Create DVR is like splitting a wire and it will make a new copy of that data in memory.

这与在 LabVIEW 中分裂一条数据线的行为本质上是一样的——它会创建数据的一份新拷贝。唯一的区别在于,DVR 创建之后可以分裂 DVR 数据线或将 DVR 传入多个 VI,此时不会创建 DVR 内部数据的额外拷贝,传递的只是引用本身(即一个指针大小的数据)。

3.3 DVR与队列的关键区别

方面

队列(Queue)

DVR

多次获取行为

Obtain Queue 传入相同名称,获取同一队列

多次 Create DVR,每次创建独立的内存区域

引用语义

多引用指向同一个队列缓冲区

每个 DVR 指向独立的内存空间

创建行为

按名称匹配,已存在则返回现有引用

每次创建新的内存分配

这个区别曾让原帖作者深感困惑:我用引用 A enqueue 一个元素,可以用引用 B dequeue 同一个元素。但 DVR 的行为是每次创建一个新实例。

3.4 In Place Element Structure 的锁机制

对 DVR 数据的读写必须在 In Place Element Structure(IPES)内部完成。IPES 扮演着关键区的角色:进入时自动加锁,确保同一时刻只有一个执行上下文能访问该 DVR 的数据;退出时自动解锁。在 IPES 右键菜单中启用 Allow Parallel Read-only Access 后,多个读操作可以同时进行,但写操作仍会阻塞所有读操作。

四、适用场景

理解 DVR 的内存分配行为后,其适用场景就变得清晰了:

场景

说明

跨循环共享大型数据结构

在并行循环间传递数组、图像、波形等,避免反复拷贝

面向对象按引用传递

将 DVR 作为数据传递的中间层,实现类似引用语义

原位修改大数据

配合 IPES 直接修改内存中的数据,无需整体拷贝

接口类型强制要求

LabVIEW 2020+ 的接口(Interface)类型强制使用 DVR

硬件资源抽象层

多个模块类共享同一物理资源(如电源机箱)的引用

五、核心要点与最佳实践

5.1正确用法:集中创建,分发引用

不要在每一个子 VI 中独立创建 DVR,而应在顶层代码中创建一次 DVR,然后将这个 DVR 数据线传递给所有需要访问的子 VI。这是解决原帖问题的关键:将创建 DVR 移到循环外部,所有子 VI 共享同一个 DVR 引用。

5.2 Restrict References 设置的作用

类的属性对话框中有一个 Restrict References of This Class to Members 选项。开启后,只有该类的成员 VI 可以创建和销毁该类的 DVR。然而需要注意:即使开启此选项,每次 Create DVR 仍然会创建新的独立数据空间。限制引用权限不等于合并数据空间。

5.3 正确的 DVR Create 方法模式

正确的 DVR Create 方法应该是幂等的:如果当前 DVR 无效则创建新的 DVR,如果当前 DVR 有效则返回现有 DVR 引用。这样可以确保整个应用程序中只有一个 DVR 指向共享数据。

六、对比分析

6.1 DVR vs其他数据共享方案

方案

数据传递

线程安全

大数据性能

调试难度

DVR

引用传递(创建时拷贝)

内置锁机制

功能全局变量(FGV)

全局存储

需自行实现

队列(Queue)

消息队列

内置

普通数据线

值传递

天然安全

6.2 DVR vs 控制引用

DVR与前后面板控件完全无关——它指向的是纯内存分配,所有读写操作在调用方线程执行,不会切换到 UI 线程。控制引用则强制在 UI 线程执行,性能开销大得多。

七、使用建议

建议

说明

集中创建 DVR

先创建好 DVR,再传递引用,不要在消费端各自创建

理解创建即拷贝

Create DVR 复制数据到新内存,不是创建指向已有数据的链接

不要为简单数据使用 DVR

对于标量或小集群,直接使用数据线或 FGV 更简洁

不在 IPES 内放阻塞操作

Wait、文件 I/O 等会长时间持有锁,阻塞其他访问者

应用实例隔离

LabVIEW 2023 Q3+ 不同应用实例不能访问彼此的 DVR

DVR不是指针的指针

不需要创建 DVR 的 DVR,多层间接引用只会增加复杂度

八、总结

DVR的常见误解根源于引用这个术语在日常语感中的歧义。理解 DVR 的关键在于牢牢记住:New Data Value Reference 永远在分配新内存、复制新数据。它不是创建一个指向现有对象的指针,而是创建一个存放数据的新位置,然后将当前值复制进去。

在这个基础上,正确的使用模式就很自然了:创建一次 DVR,然后传递这个 DVR 引用。配合 In Place Element Structure 的正确使用,DVR 可以成为大型数据共享和高性能原位操作的有力工具。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询