




已阅读5页,还剩17页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
生产者/消费者模式(1)_前言 statemice 的 LabVIEW 程序设计模式(五) 生产者/消费者模式(1)_前言再次回顾“基本状态机模式”的 6 个缺点,只剩下第 6 个缺点无法在上述的“状态机和事件结构的结合模式”中被解决。(1) 任何时刻只能有一个状态在运行这个问题也许有些多余,但是在实际的应用中往往又是最常见的。大多数比较复杂的应用至少应该有“菜单”和“采集”两个状态,如果数据采集程序在运行时仍然希望系统能够处理菜单的事件,这是在传统的状态机或者事件结构中无法实现的。因为无论是状态机结构还是事件结构,都是由一个循环组成的,不同的状态是无法同时被响应和处理的。解决这个问题的方式也比较简单,LabVIEW 本身就是一种多线程的程序设计语言,可以再加一个循环或者另外开一个程序独立运行。但是这样也会带来一些新的问题,比如:(1) 两个循环(程序)之间如何交换和共享数据。(2) 两个循环(程序)都有着独立的错误处理系统,它们之间是如何协调的。(3) 两个循环如何分工呢?应该以哪种方式对状态进行分类以将不同的状态放置在不同的循环(程序)中?(4) 一个程序如何控制另一个程序的运行和停止。在上面提出的 4 个问题中,对循环和程序这两个解决方案而言,第(1)(3)个问题的解决方式是一样的。只有第(4)个问题是专门针对两个程序而言的,在 LabVIEW 中这种不同程序之间的相互调用称为“程序的动态调用”。生产者/消费者模式(2)_VI 的可重入性(Reentrant Execution)statemice 的 LabVIEW 程序设计模式(五) 生产者/消费者模式(2)_VI 的可重入性(Reentrant Execution)在介绍 VI 的动态调用之前有必要对 LabVIEW 在执行 VI 过程中的规则有个大致的了解。众所周知,LabVIEW 是通过 VI 的文件名(VI Name)来表示独立的 VI 的,并不是 VI 的路径。因此,LabVIEW 不允许具有相同名字的 VI 同时载入内存中,即使这些 VI存储在不同的路径中。在前面曾经提到,LabVIEW 本身就是一种多线程设计的语言。那么当同一背面板中放置两个 VI 的实例时代码应该如何执行呢?在图 29 中,右侧是测试 VI 运行的时间,左侧是 Wait.vi 的具体实现代码,仅仅是等待一定的秒数。那么在右侧的 VI 中,输出的 Time 是多少呢?是 3 秒还是 2 秒?图 29 Reentrant Execution打开 VI 的 Highlight 调试工具,可以看出两个 Wait.vi 实例的调用并不是同时执行的,而是依次按顺序执行的,至于哪一个实例先执行是不确定的。这是由于 LabVIEW 本身是并行设计的,从理论上而言,两个 VI 的实例是同步执行的,但是如果两个 Wait.vi 实例同时执行必定会产生参数赋值紊乱,因为 LabVIEW 只允许内存中存在一个名称的 VI。因此,此时 Time 的输出结果是 3 秒。如果在实际使用中需要这样的应用该如何解决呢?LabVIEW 提供了 VI 的可重入技术,打开 Wait.vi,单击按键,选择图 30 所示的 Reentrant execution 选项。此时再次运行 Time 的输出结果是 2 秒。从 Highlight 的运行过程来看,两个 Wait.vi 实例是同时执行的,因此总的运行时间就是 1 秒。图 30 VI Properties事实上,LabVIEW 的可重入技术相当于在原有 VI 的基础上产生了一个相同的副本,打开 Wait.vi 从标题栏可以看出 VI 的名称为Wait.vi:1(clone)。同理这是由于 LabVIEW 中不允许内存中的 VI 存在同名,VI 的可重入技术相当于产生了与原 VI 具有同样功能的新VI 并且修改了该 VI 的命名。在实际应用中,需要根据情况决定是否设置 VI 的可重入属性,灵活使用。并不是需要将所有的 VI 都设置为可重入,那将占据大量的内存资源。生产者/消费者模式(3)_LabVIEW 程序的动态调用statemice 的 LabVIEW 程序设计模式(五) 生产者/消费者模式(3)_LabVIEW 程序的动态调用简单而言,动态调用指的是通过程序控制另外一个程序的运行、停止、赋值和获取值等。LabVIEW 提供了多种动态调用的方式,从底层而言是通过 VI Server 技术实现的。图 31 所示为 LabVIEW 中的 Application Control 选板,动态调用所使用的节点都位于这个选板。当调用一个在硬盘、内存甚至是网络路径上的 vi 时,首先要使用 Open VI Reference 以将该 VI 载入内存并获取 VI 的“句柄(Reference)”;然后再使用该句柄进行其它的控制操作;最后再关闭该 VI 的句柄避免内存泄漏,这就完成了一次对 VI 的调用。图 31 Application Control 选板图 32 是一个动态调用的具体实现代码,首先使用 Open VI Reference 获取被动态调用 VI 的 Reference(例子中是C:average.vi);再使用 Call By Reference Node 节电动态运行该 VI;最后关闭 VI 的 Reference。在使用 Call By Reference Node 时需要事先指定被调用 VI 的输入输出接口,也就是说这种动态调用的前提是必须知道被调用 VI 的输入输出接口,否则无法进行动态调用。图 32 VI 的动态调用Open VI Reference 的路径输入是一个多态的输入口,也可以使用 String 输入,如图 33 所示。此时被调用的 VI 必须在内存中,且输入的是被调用 VI 的文件名。值得一提的是这种“文件名”调用方式在可执行程序中是无法被调用的,因此建议最好采用路径的调用方式。图 33 Open VI Reference 的多态性【 应用 5】本例将使用 LabVIEW 的动态调用方式实现斐波那契数列(Fibonacci 数列)。斐波那契数列指的是这样一个数列:1,1,2,3,5,8,13,21 这个数列从第三项开始,每一项都等于前两项之和。在数学上表述为:f(n)=f(n-1)+f(n-2),其中n=3,f(1)=f(2)=1。显然这是一个比较熟悉的递归调用,但是在 LabVIEW 中似乎很难实现。由于 LabVIEW 不允许同名的 VI 同时在内存中,因此一个VI 是无法 VI 调用本身的。但是,通过 VI 的可重入技术和动态调用技术却可以实现 VI 的递归调用。图 34 所示为 Fibonacci 数列在 LabVIEW 中递归的实现方式。case 结构有两个分支,当 n=3 时,输出 f(n)=f(n-1)+f(n-2)。此时需要把 VI 设置为可重入状态。图 34 Fibonacci 数列同理我们也可以使用这种递归的方式实现 f(n)=n!的算法,从数学上可以写作 f(n)=n*f(n-1),其中 n=1,f(0)=1。具体的实例将不再详述。此外,递归算法的效率比较低,在实际应用中应谨慎使用。打开 Highlight 工具,在 Call By Reference Node 运行时,程序是处于等待状态的,只有被调用的 VI 运行完毕,主程序才会继续执行。这似乎无法解决在本节开头提到的问题,那么是否存在一种动态调用方式使被调用的 VI 与主 VI 之间分别独立运行呢?答案是肯定的。VI 本身是有很多的属性和方法的,如图 35 所示。使用这些方法就可以动态控制 VI 的运行、停止和赋值,各个属性节点和方法的具体含义见 LabVIEW 的帮助文档。使用这种方式动态调用 VI 时,并不需要知道 VI 的输入输出接口。图 35 VI Method图 36 是该使用“属性节点和方法”实现动态调用的一个实例。在大多数应用程序启动时会显示一个启动画面用来显示版权、开发单位、软件版本等信息,等待 2 秒之后关闭启动界面并启动应用程序主界面。图中使用了动态调用的方式启动主程序(Main.vi)并使主程序独立运行,首先运行程序后设置 2 秒钟的延时;其次,将启动画面的界面设置为“隐藏”(并没有退出内存,只是隐藏了前面板),并且使用 Open VI Reference 获取 VI 的句柄;然后使用FP.Open 属性打开主程序的前面板(只是打开了前面板并没有运行);使用 Run VI 方法运行主程序,将 Wait Until Done 设置为false,这样就可以保证被调用 VI 的独立运行;最后,关闭当前 VI的前面板。图 36 VI 的动态调用通过 Highlight 工具看出该 VI 的运行是独立的,并没有等待Main.vi 运行结束才继续执行。生产者/消费者模式(4)_生产者/消费者循环statemice 的 LabVIEW 程序设计模式(五) 生产者/消费者模式(4)_生产者/消费者循环本节将使用“多循环”来解决程序并行运行的问题,那么程序中的两个循环如何进行数据交互和共享呢?最普通的方式是采用全局变量或局域变量,但是当两个循环执行的速率不相等时,必然会造成数据的丢失或重复。如前所述,LabVIEW 提供了队列操作函数,允许数据的发送者和接受者之间建立一条缓冲通道,这样就避免了循环不同步带来的影响。如图 37 所示,将整个过程与供水系统进行类比,在数据产生/采集端(供水局)产生数据后,并不直接向终端用户供水,因为前者产生水的速率与后者消耗水的速率并不相同。此时需要建造蓄水池将供水局产生的水放入到蓄水池中,同理获取的数据也放入该缓冲区中。当终端用户需要用水时,直接从蓄水池中获取就可以了,同理在进行数据显示和分析时直接从数据缓冲区中获取就可以了。图 37 生产者/消费者模型当然,上面的模型也会存在一个问题:数据缓冲区/蓄水池的容量?假定供水局不停地产生自来水,而终端用户却不消耗水,这样便会导致蓄水池装满而溢出。反之当终端用户耗水量太大时,导致没有水可用。LabVIEW 中的队列函数提供了一种很好的方式规避了这个问题,由于队列中的元素是“先进先出”的,因此确保了接收到的数据是有序的。在 LabVIEW 的队列操作中(入列和出列函数),提供了 timeout 选项以处理数据缓冲区的溢出或不足。当数据溢出时,入列函数(数据进入队列)将停止发送数据(处于等待状态),直到缓冲区存在数据空间或者达到了 timeout 设置的时间;而当数据不足时,出列函数(数据流出队列)将停止接收数据(处于等到状态),直到缓冲区进入了新的数据或者达到了 timeout 设置的时间。【 应用 6】本例将演示生产者/消费者循环的一些基本特性和队列操作的特点。如图 38 所示,生产者与消费者之间传递的数据是一个连续的sine 波形,二者靠大小为 20 个点的缓冲区连接。右下角是“停止”按钮,用户控制程序的停止执行。例程提供了操作方式控件控制生产者和消费者的数据传递速率,包含五种状态:不生产,只消费、生成快于消费、生成速率等于消费速率、生成慢于消费、只生产,不消费。图 38 生产者/消费者例程的前面板图 39 所示为生产者/消费者例程的背面板,代码由 3 个循环组成,依上而下分别是生产者循环(产生 sine 数据)、消费者循环(获取 sine 数据)和状态循环(获得缓存区中数据的数据量)。例程假定正常的数据发送和接收的速率是延时 50ms,当需要某一段的速率减慢时需要将循环的延时设置为 100ms。例程将入列和出列函数的 timeout 设置为-1,表示如果没有满足条件进行入列和出列操作,循环将处于持续等待状态。在新建队列时,设置了缓冲区的大小是 20 个元素(图中的红色圆圈内)。最下面的循环是为了实时查看队列缓冲区中存储的元素数量。图 39 生产者/消费者例程的背面板运行该 VI,默认的操作方式是“生产速率等于消费速率”,从图 40 中可以看出生产者循环和消费者循环的数据是同步的,此时缓冲区内没有数据,也就是说产生的数据都被实时地消耗了。图 40 生产速率等于消费速率如果再将操作方式设置为“生成快于消费”,可以看出数据缓冲区内将逐渐变满并保持为 20 个元素。此时生产者的波形将会比消费者多 20 个数据点(这些点保存在数据缓冲区中),如图 41 所示。图 41 生产速率高于消费速率当将操作方式变为“不生产,只消费”时,生产者循环将停止生产,而消费者循环将组件消耗掉缓冲区中的数据直至数据全部消耗完(此时接收到的波形与发送的波形点一致),如图 42 所示。图 42 不生产,只消费再将操作方式变为“只生产,不消费”,消费者循环将停止消费,而生产者循环将产生数据直至数据缓冲区填满,如图 43 所示。图 43 只生产,不消费从运行过程来看,借助于 timeout 的设置,消费者接收的数据始终与生产者发送的数据是一致的,避免点数据点溢出的问题。当然,在实际使用中需要避免由于 timeout 设置为-1 而导致的无限等待和死循环。生产者/消费者模式(5)_生产者/消费者模式扩展引用statemice 的 LabVIEW 程序设计模式(五) 生产者/消费者模式(5)_生产者/消费者模式扩展结合状态机模式、事件结构和动态调用技术,能够归纳出针对较复杂应用程序的通用设计模式。对常见的测试测量程序而言,主要由数据采集、数据分析、外围菜单项响应、报表生成、数据显示这五个部分组成。其中数据采集是相对独立和长时间运行的一个模块,可以与其它的模块同时运行。因此,在大多数持续采集的程序设计中需要将它单独作为一个模块运行。与此同时,子程序也需要一条数据通道发送一些反馈命令给主程序。于是可以构成如图 44所示的一个通讯回路。图 44 通讯回路LabVIEW 提供了多种主程序与子程序之间的通讯方式,如队列、Reference、事件等。为了介绍这些方式的具体使用方法,将结合最常用的数据采集实例进行阐述。【 应用 7】本例以“计算机组件测试”为应用介绍消费者和生产者循环的具体使用方法和数据交互过程。例子并不是为了说明计算机组件测试的过程和方法,而是重在强调对该应用而言应该采用什么样的程序设计模式。因此,例子中使用了多种数据交互方式,这些交互方式的选择并不是唯一的,可以根据实际情况选择合适的数据交互方法。假设计算机的整个测试过程由 CPU、RAM、CDROM、Power.等等数项子测试项组成,程序需要充分考虑可扩展性要求,使得后期增加新的待测组件时对主程序的影响不大或者没有影响。测试过程应能够实现暂停和提前停止的功能,并且测试过程不受其它界面操作的影响。根据以上的测试要求,可以把整个测试程序分为两个部分:控制部分和执行部分。其中前者是用户主界面,用来响应用户界面事件以及控制测试流程的执行;后者是执行程序,用来根据控制命令运行测试流程并且产生测试结果。系统的结构如图 45 所示。图 45 “计算机组件测试系统”结构从上图可以看出,该应用与消费者和生产者模式是相符的,不同的是还涉及到消费者(执行部分)向生产者(控制部分)的数据传输。本例使用的是队列型的生产者和消费者模式,而反向的数据传输使用了“用户自定义事件”和“Reference”方法。当然,也可以使用队列等其它的方式。程序的主界面如图 46 所示,包括菜单栏,测试控制按钮和测试项列表 3 大部分。背面板如图 47 所示,生产者部分采用状态机和事件结构相结合的设计模式,共包含 5 大类的状态。图 46 PC Test 前面板图 47 PC Test 背面板在 PInitialize 状态中,主要实现前面板控件的初始化以及调用待测组件,如图 48 所示。为了满足测试系统的可扩展性要求,将目前的测试组件统一集中放置到 TestItems 目录中。如果后续需要增加测试组件项,只需要编写相应的测试组件代码并且放置到TestItems 目录中即可。图 48 PInitialize 状态在 DInitialize、Run、Pause 和 Stop 状态中都使用到了PCTest_Execute_Controlle
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2025年医疗器械召回管理办法考核试题及答案
- 宿舍电费分摊协议书范本
- 2025至2030中国径向轴向涡轮膨胀机行业调研及市场前景预测评估报告
- 2025年养老护理员初级考试题库及答案
- 2025年预防职务犯罪知识试题题库(附答案)
- 2025至2030中国汽车行业发展趋势分析与未来投资战略咨询研究报告
- 2025年流行病学练习题+答案
- 2025至2030中国婴幼儿配方乳粉行业发展分析及市场产业运行态势及投资规划深度研究报告
- 2025至2030中国明胶行业发展趋势分析与未来投资战略咨询研究报告
- 2025至2030中国智慧医疗行业产业运行态势及投资规划深度研究报告
- TXMSSAL 0092-2023 豆奶规范规程
- 刺五加胶囊在冠心病康复期的应用评价
- 车辆油卡充值、加油使用登记表
- 有理数计算试卷
- 文档管理系统方案
- 运用PDCA降低I类切口感染率模板课件
- 特种设备安全管理课件-电梯安全知识
- 车辆转让合同电子版下载可打印
- 深圳填海工程施工实施方案
- BB/T 0023-2017纸护角
- 建设集团有限公司安全生产管理制度汇编
评论
0/150
提交评论