




版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
reference死鎖問題
LabVIEW中的引用經(jīng)常需要和“InPlaceElementStructure”配合使用。In
PlaceElementStructure對(duì)一個(gè)引用的數(shù)據(jù)進(jìn)行解決時(shí),為了保證多線程安全,
它會(huì)鎖住引用指向的數(shù)據(jù);其它線程若需對(duì)同一數(shù)據(jù)做操作,必須能這個(gè)InPlace
ElementStructure中所有代碼執(zhí)行完畢才可,這樣就避免了多線程讀寫同一
內(nèi)存數(shù)據(jù)所產(chǎn)生的競(jìng)爭(zhēng)問題。
舉例來說,下面這段程序的執(zhí)行時(shí)間是1秒:
僵tworeference.viBlockDiagram。I回
而下面這段程序的執(zhí)行時(shí)間則是2秒:
瑞SingleData.viBlockDiagramI°I回Ifl
由于第二段程序中的兩個(gè)InPlaceElementStructure必須順序執(zhí)行。
有了“鎖住”這個(gè)操作,就有不小心導(dǎo)致死鎖的也許。比如對(duì)于同一數(shù)據(jù)的引用,千
萬不能嵌套使用InPlaceElementStructure,否則就會(huì)死鎖:
0-
在上面這個(gè)示例中,程序運(yùn)營(yíng)至內(nèi)層的InPIaceElementStructure,就會(huì)停
在這里等外層InP1aceElementStructure運(yùn)營(yíng)結(jié)束,釋放它鎖住的數(shù)據(jù);
而對(duì)于外層InP1aceElementStrueture來說,它內(nèi)部的所有代碼要運(yùn)營(yíng)結(jié)束,它
才結(jié)束。因而它們互相等待,導(dǎo)致了死鎖。
PackedProjectLibraries2與Library的比較
ackedProjectLibrary從名字上來看,就是被包裝好了的Project
Library。ProjectLibrary是編程時(shí)候由程序員創(chuàng)建出來的。比如下圖這個(gè)工
程,我在里面創(chuàng)建了一個(gè)叫做“MyAlgorithmLibrary.lvlib”的工程庫。它包含兩
個(gè)VI,其中一個(gè)是私有的。
盤ProjectExplorer-MyAlgorit..
PaekedPrejectLibrary并不是手工創(chuàng)建的,他是通過一個(gè)項(xiàng)目的生成規(guī)范,
從ProjectLibrary編譯而來的。比如上圖的項(xiàng)目,我創(chuàng)建了一個(gè)Packed
Library類型的生成規(guī)范。我在這個(gè)生成規(guī)范中指定把“MyAlgorithm
Library.Ivlib”編譯成PackedProjectLibrary?
編譯的結(jié)果是在我指定的途徑下生成了一個(gè)名為“MyA1gorithm
Library.lv1ibp”的文獻(xiàn)。它的后綴名僅比PackedLibrary多了一個(gè)字母p。
雙擊這個(gè)文獻(xiàn),可以打開它,看到他里面包含的VI:
假如需要在其它項(xiàng)目中使用到這個(gè)PackedProjectLibrary,我們可以直接把
它加到另一個(gè)項(xiàng)目中去,下圖是一個(gè)演示項(xiàng)目:
PackedProjectLibrary看上去和ProjectLibrary非常相似,用
法也完全相同。
PackedProjectLibrary與ProjectLibrary
?都是將功能相關(guān)的一組VI封裝起來的方法;
?庫中的VI可以具有層次機(jī)構(gòu);
?庫中的VI都帶有名字空間,名字空間是帶有后綴名的庫名;
?都可以方便的放在項(xiàng)目管理器里使用
盡管它們十分相似,PackedProjectLibrary與ProjectLibrary相比,還
是有一些明顯區(qū)別的:
?PaekedProjectLibrary是通過編譯生成的;
?PackedProjectLibrary中的VI是編譯后產(chǎn)生的,它們不能被修
改;
?PackedProjectLibrary包具有私有VI,但用戶無法看到也不能
使用它們;
?PackedProjectLibrary把VI,.Ivlib以及其它用到的文獻(xiàn)都打
成一個(gè)壓縮包,用戶在磁盤上就只能看到一個(gè).1vlibp文獻(xiàn),看不到VI文
獻(xiàn);
?PackedProjectLibrary很適合作為最終產(chǎn)品發(fā)布給用戶使用;
?在項(xiàng)目中使用PackedProjectLibrary可以縮短編譯時(shí)間,由于
PackedProjectLibrary中的VI是已編譯好的,不會(huì)再隨項(xiàng)目編譯一
遍。(這一條先這樣寫上,但我還需要再進(jìn)一步研究一下)
LabVIEW中LVCIass數(shù)據(jù)轉(zhuǎn)換成XML格式的問題
前一段時(shí)間,一個(gè)同事的程序出了問題。他在程序中把一個(gè)LVCIass類型的數(shù)據(jù)
轉(zhuǎn)換成XML格式,再保存成文獻(xiàn)。但是從文獻(xiàn)中把數(shù)據(jù)轉(zhuǎn)回成LVC1ass時(shí),
卻出了問題:在調(diào)用“UnflattenXML”這個(gè)函數(shù)時(shí),程序有時(shí)犯錯(cuò),有時(shí)又不
犯錯(cuò)。他的程序中使用了大量的LVCIass,并且它們之間有著復(fù)雜的繼承與包含
關(guān)系,以至于花了兩三天的事件,才找出問題所在。其實(shí)是個(gè)簡(jiǎn)樸的問題,只是在
設(shè)計(jì)程序時(shí)他沒故意識(shí)到。
我做了一個(gè)簡(jiǎn)化的程序,可以重現(xiàn)這個(gè)問題:
一方面,給一個(gè)子類的對(duì)象設(shè)立一些數(shù)據(jù)。然后把它當(dāng)做父類類型的數(shù)據(jù),平化成
XML文本,存盤:
E
關(guān)閉LabVIEW,然后重新打開LabVIEW。再編寫一個(gè)反向程序,把XML數(shù)據(jù)轉(zhuǎn)
換成父類類型的數(shù)據(jù):
發(fā)現(xiàn)UnflattenFromXML函數(shù)返回一個(gè)錯(cuò)誤,value中是一個(gè)空的數(shù)據(jù)。
錯(cuò)誤產(chǎn)生因素如下:在把子類數(shù)據(jù)轉(zhuǎn)換成父類數(shù)據(jù)類型,這個(gè)類型雖然是父類的,但
其數(shù)據(jù)仍然是子類的。再轉(zhuǎn)換成XML格式,XML格式中記錄的仍然是子類的數(shù)
據(jù)。
在反向過程中,UnflattenFromXML拿到的數(shù)據(jù)是子類的,但它企圖轉(zhuǎn)換時(shí),
卻發(fā)現(xiàn)內(nèi)存中沒有子類的類型信息,因此它也就不知道如何轉(zhuǎn)換這個(gè)數(shù)據(jù),所以報(bào)
錯(cuò)。
假如這個(gè)程序稍微改動(dòng)一下,把XML數(shù)據(jù)直接轉(zhuǎn)換成子類的數(shù)據(jù),就不會(huì)犯錯(cuò)了:
事實(shí)上,子類的數(shù)據(jù)總是可以用父類來表達(dá)的。因此這個(gè)XML數(shù)據(jù)亦可以直接被
轉(zhuǎn)換成父類的類型,但前提是,-定要保證子類的類型別家在到內(nèi)存中去了。只要
在程序中放置一個(gè)子類的對(duì)象,自然就可以把子類加載至內(nèi)存。像下面這個(gè)程序就
可以正常工作:
a
這個(gè)實(shí)驗(yàn)反映出兩個(gè)問題:
1.把XML中的內(nèi)容假如是屬于某個(gè)LVClass類型的數(shù)據(jù),把這些數(shù)據(jù)轉(zhuǎn)換回
LVClass數(shù)據(jù)時(shí),那個(gè)LVC1ass一定要已經(jīng)存在于內(nèi)存才行。
2.在之前的一篇文章“LvClass的一個(gè)效率問題”中提到過:當(dāng)子類被加載如
內(nèi)存時(shí),它所有的父類也會(huì)被加載入內(nèi)存。但反過來并不成立。由于一個(gè)類
有哪些父類是擬定的,父類的地址就記錄在子類中。但一個(gè)類并不知道他會(huì)
有多少子類,任何人都可以從它派生出不同的子類來,因此它在裝入內(nèi)存
時(shí),不也許把自己的子類也都裝進(jìn)來。
LabVIEW中實(shí)現(xiàn)鏈表、樹等數(shù)據(jù)結(jié)構(gòu)
LabVIEW自帶的數(shù)據(jù)結(jié)構(gòu)只有數(shù)組和隊(duì)列。多數(shù)情況下,這兩種數(shù)據(jù)結(jié)構(gòu)足夠開
發(fā)者使用了。但是,我平時(shí)使用C++和C#語言更多一些,所以編寫程序時(shí)經(jīng)常會(huì)想
到使用其它編程語言中常見的數(shù)據(jù)結(jié)構(gòu)比如鏈表(List)、樹(Tree)等。
LabVIEW中也可以編程實(shí)現(xiàn)這些數(shù)據(jù)結(jié)構(gòu),一個(gè)比較直觀易懂的編程方法是基于
LabVlEW中的類和引用來實(shí)現(xiàn)各類數(shù)據(jù)結(jié)構(gòu)。我在《我和LabVlEW》一書的第
13.3.5節(jié)中介紹了一個(gè)簡(jiǎn)樸的鏈表容器的實(shí)現(xiàn)方法,它是基于LvClass編寫的,數(shù)
據(jù)流驅(qū)動(dòng)的一種容器。但是正如我在書中提到的,它雖然和有一些和文本編程語言
中的鏈表相類似的地方,但本質(zhì)并不相同。文本編程語言中的鏈表,樹等數(shù)據(jù)結(jié)構(gòu)
離不開引用(或指針),節(jié)點(diǎn)之間是通過引用來互相關(guān)聯(lián)的。LabVlEW可認(rèn)為
數(shù)據(jù)創(chuàng)建引用,因此也可以方便的實(shí)現(xiàn)與文本語言中功能相同的數(shù)據(jù)結(jié)構(gòu)。
這里插一段,介紹一下數(shù)據(jù)結(jié)構(gòu)和數(shù)據(jù)容器的關(guān)系,我自己理解是這樣的:數(shù)據(jù)結(jié)
構(gòu)側(cè)重于數(shù)據(jù)的存儲(chǔ)方式,比如如何排序;數(shù)據(jù)結(jié)構(gòu)在加上與此結(jié)構(gòu)相關(guān)的操作
方法,比如添加刪除數(shù)據(jù)等方法,就構(gòu)成了一個(gè)數(shù)據(jù)容器。脫離了操作方法,單純的
數(shù)據(jù)結(jié)構(gòu)用處非常有限。因此,我文章中在提到數(shù)據(jù)結(jié)構(gòu)或者數(shù)據(jù)容器時(shí),指的
都是同一回事:數(shù)據(jù)結(jié)構(gòu)和相關(guān)的方法。
為了介紹如何在LabVIEW中實(shí)現(xiàn)一個(gè)數(shù)據(jù)結(jié)構(gòu),我打算以雙向鏈表為例,講解
一下如何編寫它。
雙向鏈表中每個(gè)節(jié)點(diǎn)都會(huì)記錄上一個(gè)節(jié)點(diǎn)和下一個(gè)節(jié)點(diǎn)的位置。因此,在雙向鏈表
中,可以從一個(gè)節(jié)點(diǎn)直接跳轉(zhuǎn)到它的上一個(gè)或下一個(gè)節(jié)點(diǎn)上去,也就是正向或反向
遍歷整個(gè)鏈表??梢灾庇^的想到,使用LvC1ass實(shí)現(xiàn)這樣的節(jié)點(diǎn),只要為這個(gè)
節(jié)點(diǎn)創(chuàng)建一個(gè)類ListNode,并且這個(gè)類有兩個(gè)成員變量,它們的類型都是
ListNode的引用,分別用于指向前一個(gè)和后一個(gè)節(jié)點(diǎn)就可以了:
ListNode
Data:int
Previous:ListNode
Next:ListNode
這樣的設(shè)計(jì)在文本編程語言中是沒有問題的,但在LabVIEW中行不通。其它
編程語言中,程序運(yùn)營(yíng)時(shí),才會(huì)對(duì)類的對(duì)象進(jìn)行初始化。LabVIEW中,VI一
打開,它上面的控件和常量就需要被初始化了。某個(gè)對(duì)象在初始化時(shí),它的成員變量
也要被初始化,若它的成員變量的類型還是這個(gè)類,這以初始化的過程就陷入了死
鎖:類需要它的成員變量先初始化;它成員變量需要這個(gè)類先初始化。
基于同樣的因素,一個(gè)類的成員變量的數(shù)據(jù)類型也不可以是這個(gè)類的子類:子類初
始化需要先對(duì)它的父類進(jìn)行初始化。但是,一個(gè)類的成員變量的數(shù)據(jù)類型可以是這
個(gè)類的父類:父類在初始化的時(shí)候,不需要理睬它的任何子類。
既然父類初始化時(shí),不依賴于子類的初始化;而子類的對(duì)象又可以被當(dāng)做父類的類
型來保存,咱們就可以運(yùn)用這一特性在LabVIEW中實(shí)現(xiàn)可以數(shù)據(jù)結(jié)構(gòu)的節(jié)點(diǎn)
了。只但是LabVIEW實(shí)現(xiàn)鏈表的節(jié)點(diǎn)要多一個(gè)環(huán)節(jié):我們需要為L(zhǎng)istNode類
再定義一個(gè)父類ListNodeVirtuaL這個(gè)父類不做任何實(shí)質(zhì)性的工作,它僅用
于保存相鄰節(jié)點(diǎn)的引用。
ListNodeVirtual
A
ListNode
Data:int
Previous:ListNodeVirtual
Next:ListNodeVirtual
以上兩個(gè)類是針對(duì)鏈表節(jié)點(diǎn)的雙向鏈表自身也需要做成一個(gè)類:
DoubleLinkedList類,這個(gè)類中封裝有鏈表的屬性和方法。比如它需要一個(gè)指向
鏈表表頭的引用,需要有為鏈表添加刪除數(shù)據(jù)的方法,為遍歷鏈表中的數(shù)據(jù),還需要
有一個(gè)迭代器……
作為演示,我只實(shí)現(xiàn)了鏈表的幾個(gè)簡(jiǎn)樸功能。演示程序工程結(jié)構(gòu)如下:
現(xiàn)ProjectExplorer-Listlvproj[o|口
FileEditViewProjectOperateToolsWindowHe
II七亡品IX哈gxll]富胃|畫▼番二
ItemsFiles
B-鼠Project:List.lvproj
0-5MyComputer
申?,ListNodeVirtual.IvcIass
7■?ListNode.IvcIass
:ListNode.ctl
j-Readdata.vi
沁圖Writedata.vi
[■-@Readprevious.vi
其Writeprevious.vi
k電Readnext.vi
'?-'?Writenext.vi
0,@DoubleLinkedListlvcIass
k?'DoubleLinkedList.ctl
;???圖AppendafterEnumerator.vi
????@GetAllData.vi
h@EnumeratorgoNext.vi
9?EnumeratorValue.vi
?凰,ResetEnumerator.vi
“反Demo.vi
k七'Dependencies
金B(yǎng)uildSpecifications
ListNode的成員變量涉及一個(gè)數(shù)據(jù),和兩個(gè)指向前后節(jié)點(diǎn)的引用:
DoubleLinkedList類的成員變量涉及指向鏈表頭節(jié)點(diǎn)的引用,迭代器指向的節(jié)點(diǎn)的
引用,并記錄了鏈表長(zhǎng)度
器DcuhlHink-一I=I回?ESjj
下面看一下鏈表中幾個(gè)重要方法是如何實(shí)現(xiàn)的。
一方面是AppendafterEnumerator.vi這個(gè)方法,它是鏈表里最復(fù)雜的一個(gè)
方法。它的輸入是鏈表中一個(gè)新的節(jié)點(diǎn),它把這個(gè)新節(jié)點(diǎn)添加在鏈表迭代器指向的
那個(gè)節(jié)點(diǎn)的后面。
在給鏈表添加數(shù)據(jù)時(shí),會(huì)碰到兩種情況。一方面,這個(gè)鏈表是一個(gè)空鏈表,那么被添
加的節(jié)點(diǎn)就是這個(gè)鏈表的首節(jié)點(diǎn),鏈表的迭代器也應(yīng)當(dāng)指向這一唯一的節(jié)點(diǎn)。
我設(shè)計(jì)的這個(gè)鏈表是一個(gè)環(huán)狀鏈表。當(dāng)鏈表中只有一個(gè)節(jié)點(diǎn)的時(shí)候,這個(gè)鏈表的上
一個(gè)和下一個(gè)節(jié)點(diǎn)都是它自己。
假如鏈表不是空的,就把新節(jié)點(diǎn)插在迭代器指向的節(jié)點(diǎn)的后面。因此:
新節(jié)點(diǎn)的前一節(jié)點(diǎn)指向的應(yīng)當(dāng)是迭代器指向的那個(gè)節(jié)點(diǎn);新節(jié)點(diǎn)的后一節(jié)點(diǎn)是迭代
器指向節(jié)點(diǎn)本來的后一節(jié)點(diǎn)。迭代器指向節(jié)點(diǎn)的新的后一節(jié)點(diǎn)應(yīng)當(dāng)是這個(gè)新節(jié)點(diǎn);
本來迭代器的后一節(jié)點(diǎn)的前一節(jié)點(diǎn)也應(yīng)當(dāng)換成這個(gè)新的節(jié)點(diǎn)。最后,我把迭代器也
指向了這個(gè)新的節(jié)點(diǎn),這樣連續(xù)添加新節(jié)點(diǎn)時(shí),它們會(huì)按照先后順序插入鏈表。
我的演示程序還用到了其它幾個(gè)方法。
ResetEnumerator.vi負(fù)責(zé)把迭代器復(fù)位,也就是指向鏈表的頭節(jié)點(diǎn):
HNoError
ListinListout
firstfirst
TQ自鳴Ll|
enumeratorenumerator
errorin(noerror)errorout
匚■4E]
EnumeratorgoNext,vi用于讓迭代器向后移動(dòng)一個(gè)節(jié)點(diǎn):
EnumeratorValue.vi返回迭代器指向的那個(gè)節(jié)點(diǎn):
使用這幾個(gè)方法就可以搭建出一個(gè)簡(jiǎn)樸的演示程序來看一下鏈表如何工作了。下面
這個(gè)演示程序中,分兩部分:第一部分是左面那個(gè)循環(huán),每次循環(huán)迭代就會(huì)創(chuàng)建出
一個(gè)新的ListNode對(duì)象,它的數(shù)值是當(dāng)前迭代的次數(shù);右半部分使用鏈表的迭代
器遍歷鏈表中的節(jié)點(diǎn)。在這個(gè)演示程序中,迭代器移動(dòng)次數(shù)比鏈表長(zhǎng)度多了兩次,
由于鏈表是環(huán)狀的,轉(zhuǎn)著圈訪問,鏈表中的頭兩個(gè)元素會(huì)被讀出兩遍。
程序運(yùn)營(yíng)后,data顯示了迭代器每一步所指向的節(jié)點(diǎn)的值:
data
|0|1|2|3|4_|0|1
回調(diào)VI
LabVIEW界面程序最常用的結(jié)構(gòu)就是循環(huán)事件結(jié)構(gòu)。用事件結(jié)構(gòu)截獲用戶在界面
上對(duì)控件的操作,然后做出相應(yīng)解決。
在文本語言中,常用的事件解決方法與LabVIEW是不同的。文本語言經(jīng)常使用
回調(diào)函數(shù)來解決界面事件。比如:某個(gè)按鈕按下時(shí),需要做一個(gè)fft運(yùn)算。那
么就寫一段函數(shù)來完畢這個(gè)fft運(yùn)算,再把這個(gè)函數(shù)與按鈕按下事件關(guān)聯(lián)起來。
開發(fā)語言通常已經(jīng)做好了對(duì)事件的監(jiān)控,一旦發(fā)現(xiàn)按鈕按下事件產(chǎn)生了,就去調(diào)用
與它關(guān)聯(lián)的fft運(yùn)算函數(shù)。這個(gè)有開發(fā)者編寫,被系統(tǒng)調(diào)用的函數(shù)就叫做回調(diào)函
數(shù)。
LabVIEW也可以采用與文本語言相類似的方法來解決事件:不是在事件結(jié)構(gòu)內(nèi)解
決,而是在程序開始時(shí),就為某事件注冊(cè)一個(gè)回調(diào)VI。在回調(diào)VI內(nèi)編寫相應(yīng)代
碼,一旦事件發(fā)生,這段代碼就會(huì)被執(zhí)行。
與事件結(jié)構(gòu)相比,回調(diào)VI編寫起來稍微麻煩一點(diǎn);但它的好處是,它和主VI是平
行運(yùn)營(yíng)的。假如事件解決過程比較耗時(shí),把它放在事件結(jié)構(gòu)中會(huì)阻塞整個(gè)程序,使
得程序界面暫時(shí)失去響應(yīng);而把它放在回調(diào)VI中,則不會(huì)影響程序其它部分的運(yùn)
營(yíng)。
比如下面這個(gè)例子。程序界面上有兩個(gè)儀表盤:左面那個(gè)始終在運(yùn)轉(zhuǎn),每10秒鐘旋
轉(zhuǎn)一圈;右邊那個(gè),由按鈕控制,按下按鈕才旋轉(zhuǎn)一圈。若把旋轉(zhuǎn)右表這個(gè)工作放到
事件結(jié)構(gòu)的按鈕按下解決分支中去做,它勢(shì)必會(huì)打斷左表的旋轉(zhuǎn),因此,考慮把它放
到回調(diào)VI中去做。
這是主程序界面:兩個(gè)表盤,和一個(gè)控制右表旋轉(zhuǎn)的按鈕。
程序的代碼也比較簡(jiǎn)樸。先看代碼的右半部份:這是一個(gè)典型的循環(huán)事件結(jié)構(gòu),用
來控制左表的旋轉(zhuǎn)。但是注意,右表的控制并不是在這個(gè)結(jié)構(gòu)中實(shí)現(xiàn)的。
再看程序左半部分:它為按鈕“右表旋轉(zhuǎn)一圈”的值改變事件注冊(cè)了一個(gè)回調(diào)VI。
注冊(cè)回調(diào)VI用的是節(jié)點(diǎn)“RegisterEventCa1Iback”,它在函數(shù)選板
“Connectivity-〉A(chǔ)ctiveX”上。這個(gè)節(jié)點(diǎn)重要是為了給ActiveX、.NET控
件的事件注冊(cè)回調(diào)VI。事件結(jié)構(gòu)無法截獲ActiveX、.NET控件的事件,因此只
能通過回調(diào)VI的方式來解決這些控件的事件。但是這個(gè)節(jié)點(diǎn)也可以用于給
LabVIEW自帶的控件注冊(cè)回調(diào)VIo
注冊(cè)回調(diào)VI節(jié)點(diǎn),有三個(gè)輸入?yún)?shù)從上至下分別是:事件的發(fā)出者、回調(diào)VI、
用戶自定義數(shù)據(jù)。
在我們這個(gè)例子中,需要截獲的是按鈕“右表旋轉(zhuǎn)一圈”的值改變事件,因此需要把
“右表旋轉(zhuǎn)一圈”控件的引用作為第一個(gè)參數(shù)傳遞給注冊(cè)回調(diào)VI節(jié)點(diǎn)。指定好
事件的發(fā)出者,接下來需要選擇事件的類型,鼠標(biāo)點(diǎn)擊注冊(cè)回調(diào)VI節(jié)點(diǎn)的第一個(gè)
參數(shù)的接線方塊,發(fā)現(xiàn)“右表旋轉(zhuǎn)一圈”按鈕的所有事件都已經(jīng)列在這里T,選擇''值
改變”事件。
第三個(gè)參數(shù)是用戶自定義數(shù)據(jù),可以是任意類型的數(shù)據(jù),在回調(diào)VI中需要用到的
數(shù)據(jù)都可以通過它來傳遞。由于我打算在回調(diào)VI中對(duì)控件“右表”做修改,因此,
在這里把“右表”的引用作為數(shù)據(jù)傳遞給回調(diào)VI。
第二個(gè)參數(shù)是回調(diào)VI的引用,假如已經(jīng)寫好了回調(diào)VI,把引用傳進(jìn)去就行了。
我還沒有編寫回調(diào)VI,因此可以在參數(shù)的接線端上點(diǎn)擊鼠標(biāo)右鍵,選擇
“CreateCal1backVI”創(chuàng)建一個(gè)空白的回調(diào)VI。
回調(diào)VI中寫一小段代碼,讓右表旋轉(zhuǎn)一圈,整個(gè)程序就完畢了。這時(shí),左右表
可以各自運(yùn)營(yíng),互不影響。
LvClass的一個(gè)效率問題
前幾天,聽到了一個(gè)客戶的抱怨:他編寫了一個(gè)LabVIEW程序,每次打開主程序
就要花費(fèi)幾分鐘的時(shí)間,這有點(diǎn)令他忍無可忍。我沒有見過他的源程序,但是據(jù)幫
他檢查過程序的同事講,他的問題很也許是使用了大量的LvClass導(dǎo)致的。在他
的項(xiàng)目中,包具有上百個(gè)類(LvClass)o我以前也聽說過LvC1ass在效率上
也許會(huì)有些問題,聽到了這個(gè)消息后,我自己做了一個(gè)實(shí)驗(yàn)。
LabVIEWSeripting中有一個(gè)屬性節(jié)點(diǎn)可以用來查看內(nèi)存中所有的VI,我就運(yùn)
用這個(gè)VI來查看一個(gè)程序到底在裝入些什么,令它啟動(dòng)如此之慢。
假設(shè)不存在子VI,假如打開某個(gè)不在LvClass中的VI(即便這個(gè)VI是屬于某個(gè)
Ivlib的),只有這個(gè)VI會(huì)被裝入內(nèi)存。但是,打開某一個(gè)LvClass中的VI,
我發(fā)現(xiàn)不僅這個(gè)VI會(huì)被裝入內(nèi)存,它所在的類中的所有其它的VI也都被調(diào)入內(nèi)
存。假如這個(gè)類尚有父類和祖先類,那么所有父類、祖先類中的VI統(tǒng)統(tǒng)都會(huì)被
調(diào)入內(nèi)存。
總結(jié)一下就是這樣:當(dāng)一個(gè)VI被裝入內(nèi)存
1.它的所有子VI都會(huì)被裝入內(nèi)存;
2.它所在的類中的所有的VI都會(huì)被裝入內(nèi)存;
3.它所在的類的父類中的所有的VI都會(huì)被裝入內(nèi)存。
以上3條可以是遞歸發(fā)生的,比如一個(gè)主VIA被裝入內(nèi)存,它的子VIB也會(huì)被
裝入內(nèi)存,和B同屬一個(gè)類的VIC也要被裝入內(nèi)存,C中有個(gè)子VID,D屬于類
E,E有個(gè)父類F,F中有個(gè)方法VIGo盡管G的功能和程序A八桿子都打不著
了,但也會(huì)被裝進(jìn)來。這大約就是那個(gè)用戶碰到的問題,表面上他的程序不算太大,
但是程序開始啟動(dòng)時(shí),卻需要把多于程序自身數(shù)倍的不相關(guān)的VI都裝入內(nèi)存,這一
過程會(huì)每次都浪費(fèi)他幾分鐘的時(shí)間。
鑒于LvClass的這一特性,設(shè)計(jì)使用它的時(shí)候一定要格外小心,否則很也許會(huì)導(dǎo)
致程序效率的低下。我想到了幾點(diǎn)需要注意的地方:
1.假如僅需要對(duì)一些VI進(jìn)行封裝,那么應(yīng)當(dāng)使用Ivlib,而不是1vclasso
兩者封裝的重要區(qū)別是,Ivclass可以封裝對(duì)象的屬性(也就是模塊用到
的數(shù)據(jù))。
2.類中的VI必須是高內(nèi)聚的,類中的方法共同完畢某一基本功能,不可再分
害U。應(yīng)用程序一旦用到這個(gè)類中的某個(gè)VI,就意味著程序?qū)?huì)使用到類中
幾乎所有的VI;而不是一個(gè)應(yīng)用程序也許只使用這個(gè)類中的某幾個(gè)VI。
3.繼承關(guān)系應(yīng)當(dāng)盡量簡(jiǎn)樸。沒有必要的時(shí)候盡量不使用繼承。LabVIEW不
支持接口,不應(yīng)創(chuàng)建一個(gè)純虛類,然后當(dāng)作接口來用。
4.盡量不要嵌套調(diào)用。比如在一個(gè)類的VI中又去調(diào)用另一個(gè)類中的VI。
5.打算使用多態(tài)這個(gè)特性時(shí)要注意,多態(tài)使得應(yīng)用程序在運(yùn)營(yíng)時(shí),根據(jù)對(duì)象的
類型選擇相應(yīng)的解決方法。但有些選擇應(yīng)當(dāng)是程序編譯時(shí)就做出的,它們不
適合套用在多態(tài)特性上。
舉一些例子:
?INI文獻(xiàn)讀寫這個(gè)模塊比較適合做成類,每個(gè)INI文獻(xiàn)相應(yīng)一個(gè)類的實(shí)
例。它有豐富的數(shù)據(jù)(文獻(xiàn)的內(nèi)容);它的方法有限,基本上只需要打開、
讀條目、寫條目、保存關(guān)閉,這四個(gè)方法,并且一般的應(yīng)用程序都會(huì)同時(shí)使
用到這四個(gè)方法。
?復(fù)雜儀器的驅(qū)動(dòng)程序不適合做成類。由于驅(qū)動(dòng)程序會(huì)提供非常多的功能,示
波器有各種觸發(fā)模式。而一個(gè)應(yīng)用程序通常只用到多種模式中的某一種就夠
用了。
?某測(cè)試程序可以生成測(cè)試報(bào)告給用戶。用戶可以選擇幾種不同的報(bào)告類型。
生成報(bào)告的模塊可以用Ivc1ass來設(shè)計(jì)。由于生成不同類型的報(bào)告的方法
間,可重用代碼很多,可認(rèn)為它們?cè)O(shè)計(jì)一個(gè)基類。并且,是程序運(yùn)營(yíng)時(shí),才選
擇生成報(bào)告類型的。
?某一測(cè)試程序,可以支持多種型號(hào)的儀器。由于不同用戶使用不同的硬件。
對(duì)不同型號(hào)儀器的支持不適合使用Ivclass來設(shè)計(jì),由于測(cè)試程序發(fā)布給用
戶時(shí).,用戶的硬件設(shè)備是固定的。對(duì)儀器的選擇應(yīng)當(dāng)是程序發(fā)布時(shí)就決定好
的,而不應(yīng)等到程序每次運(yùn)營(yíng)起來后判斷。
如何在程序中同時(shí)彈出多個(gè)子VI的界面,各自運(yùn)營(yíng)互不影響
回答網(wǎng)友一個(gè)問題:“我設(shè)計(jì)了一個(gè)labview界面子VI,我想在主VI中多次調(diào)用
該界面VI(同時(shí)執(zhí)行,單獨(dú)分派內(nèi)存),并顯示出多窗口,該如何設(shè)立?我嘗試將
子VI屬性設(shè)立成可重入,仍無法解決?!?/p>
這個(gè)問題其實(shí)挺常見的。若需要子VI打開多份實(shí)例,子VI必須是可重入的。所
以第一步要把子VI設(shè)立為可重入。但僅僅這樣還不夠,主程序運(yùn)營(yíng)到子VI
處,把子VI打開后,會(huì)一直等在這里,知道子VI運(yùn)營(yíng)結(jié)束,才繼續(xù)執(zhí)行主VI后
續(xù)的代碼。主VI既然已經(jīng)停在這里了,自然不會(huì)再繼續(xù)去打開其它子VI。解決的
辦法是在調(diào)用子VI的地方,改為動(dòng)態(tài)調(diào)用,并且不等待子VI運(yùn)營(yíng)結(jié)束。這樣一
來,主程序運(yùn)營(yíng)到這里,將子VI調(diào)起后,立即執(zhí)行后續(xù)代碼,又可以去調(diào)用其它的
子VI了。
需要注意的是,用于子VI是可重入的,需要給“OpenVIReferenee”函數(shù)設(shè)立一個(gè)
值為“8”的Options參數(shù)。主VI程序代碼如下:
|0x08:Prepareforreentrantrun.|
LabVIEW2023新功能-傳引用
以前版本的LabVIEW雖然也有多種方法可以讓數(shù)據(jù)以引用的方式在程序間傳遞,
但是用起來都有些麻煩。LabVIEW有了構(gòu)建數(shù)據(jù)傳引用的節(jié)點(diǎn),大大簡(jiǎn)化了傳引
用的程序代碼。
新添的有關(guān)傳引用的兩個(gè)節(jié)點(diǎn)在函數(shù)選板“Programing->Application
Control—>MemoryControl”中,分別是“NewDataValueReference"和"Delete
DataValueReferenee"。"NewDataVa1ueReference”用于創(chuàng)建一個(gè)數(shù)據(jù)的
引用,“DeleteDataVaiueReference”可以從引用中取回本來的數(shù)據(jù)。
引用最重要應(yīng)用于多線程程序中。假如兩個(gè)線程同時(shí)對(duì)同一份數(shù)據(jù)進(jìn)行修改,則必
須使用傳引用的機(jī)制。否則,使用值傳遞的方式,數(shù)據(jù)在數(shù)據(jù)線分叉的地方,就會(huì)編
程獨(dú)立的兩份,之后在兩個(gè)線程內(nèi)分別修改的是兩份完全獨(dú)立的數(shù)據(jù),沒辦法對(duì)同
一份數(shù)據(jù)進(jìn)行修改的。
比如下圖這個(gè)程序,程序輸入了一個(gè)數(shù)組,然后需要在兩個(gè)并行的子VI中同時(shí)對(duì)
這個(gè)數(shù)組中的數(shù)據(jù)進(jìn)行修改。每個(gè)子VI也許修改了數(shù)組不同的元素,程序運(yùn)營(yíng)結(jié)
束產(chǎn)生的數(shù)組應(yīng)當(dāng)把兩個(gè)子VI中的修改都包含進(jìn)來。因此,程序一開始需要數(shù)組
數(shù)據(jù)生成一個(gè)引用,然后把引用分別傳遞到兩個(gè)子VI中去。兩個(gè)子VI都運(yùn)營(yíng)結(jié)
束后,在從引用中取回?cái)?shù)據(jù)。
LabVIEW中已有的函數(shù)還都是為值傳遞設(shè)計(jì)的,所以使用值傳遞少不了把數(shù)據(jù)取
出、放回的過程。這以過程中,也許又會(huì)產(chǎn)生數(shù)據(jù)拷貝,效率會(huì)比較差。好在我們
可以使用“InPlaceE1ementStructure”結(jié)構(gòu)來解決從引用中取出、放回?cái)?shù)據(jù)
的過程。配合了“InP1aceElementStructure”結(jié)構(gòu)使用后,LabVIEW會(huì)盡
量使用數(shù)組原地址,而不是把從引用中取出的數(shù)據(jù)復(fù)制一份,這樣就做到了傳引用與
效率的兼顧。比如下面兩圖中的程序,功能是完全相同的,但LabVIEW會(huì)對(duì)下
面一幅圖中的程序進(jìn)行優(yōu)化,提高效率。
daU丫山,referencevtluereferenceout
美化程序-隱藏程序框圖上的大個(gè)cluster
在編寫某些程序的時(shí)候也許會(huì)碰到如圖1所示的情形:即用到了一個(gè)極為復(fù)雜的數(shù)
據(jù)類型常量。這個(gè)常量由于體積巨大,使得在程序框圖無論怎么擺放都讓人看起來
不太舒適。如何才干把這個(gè)程序改造得美觀一些呢?
圖1:體積巨大的常量會(huì)有礙觀瞻
要解決這個(gè)問題,只有設(shè)法把這個(gè)常量在主程序框圖上隱藏起來。通常可以用以下
兩種方法。
第一種方法:把這個(gè)常數(shù)變換成控件,再把控件隱藏起來。這種方法比較簡(jiǎn)樸,但是
也有弊病。①容易引起誤解:控件一般表達(dá)有值傳入,其別人讀程序讀到這里就
也許搞不清楚這個(gè)值是從哪里傳來的了;②假如要修改常量Cluster中某一個(gè)元素
的值,操作起來比較麻煩。
第二種方法,也就是我向大家推薦的:把它隱藏到更深層的子VI中去。具體操作
方法如下:
如圖2先給這個(gè)復(fù)雜數(shù)據(jù)類型建立一個(gè)StrictTypeDef。我的建議是為所
有程序中用到的Ouster都建立一個(gè)StrictTypeDefo這樣可認(rèn)為以后的程
序維護(hù)省去很多麻煩。
圖2:StrictTypeDef.
然后然后再建立一個(gè)新的VI,把我們要隱藏的這個(gè)個(gè)頭巨大的常量擺放在這個(gè)VI
中,并且連接一個(gè)Indicator,以把它的值傳出來。VI的接線板采用4-2-2-4格
式的,最下層第3個(gè)接線端用于傳出VI中唯一的數(shù)據(jù),如圖3所示。
6clusterdato.viFrontPanel上叵『X
圖3:用于隱藏個(gè)頭巨大常量的VI
這個(gè)VI的圖標(biāo)要做得小巧美麗,如圖4,圖標(biāo)不一定非要做成正方形。只要
B&W和256Colors中的圖標(biāo)形狀同樣,我們就可以畫出不規(guī)則圖標(biāo)了。具
體方法可以參考《制作不規(guī)則圖形的子VI圖標(biāo)》。
圖4:常量數(shù)據(jù)VI的圖標(biāo)
把這個(gè)新造出來的常量數(shù)據(jù)VI拖到程序框圖上,把它的輸出鏈接到剛才鏈接常量
的地方,再把位置擺放好。現(xiàn)在我們的程序是不是美麗多了
圖5:改造后的程序框圖
Caption和Labe1的書寫規(guī)范
LabVIEW控件的Caption和Label的特性和用途很相似,都是給了控件一個(gè)故
意義的名字。因此,在很多場(chǎng)合沒有必要刻意區(qū)分他們。
Caption和Label的最重要區(qū)別在于,Caption可以在程序運(yùn)營(yíng)的時(shí)候改變;而
Label則不可以,一旦程序運(yùn)營(yíng),就固定不變了。鑒于這一點(diǎn),Caption和
Labe1的用途也略有區(qū)別。Label應(yīng)當(dāng)是給程序自己用的,比如在程序中需要根
據(jù)控件的名字找到它,那就得跟據(jù)Label來找,而不能用Caption來找;
Caption是為了給用戶看的,有時(shí)控件的名字在運(yùn)營(yíng)到不同狀態(tài)下需要發(fā)生改變,
此時(shí)顯示在界面上的就應(yīng)當(dāng)是Caption()
推薦大家按照下面的規(guī)范使用C叩tion和Label。
先給VI分一下類:
1.底層VI:用戶不會(huì)直接使用到的VI,作為subVl隨程序一起發(fā)布。
2.用戶界面VI:VI前面板是給用戶看的程序界面的一部分。
3.程序接口VI:VI是提供應(yīng)用戶,在他們編程時(shí),當(dāng)作API被調(diào)用。
對(duì)于Caption和Label一個(gè)共同的書寫規(guī)范是:使用故意義的文字,在使
用英語短語命名時(shí).,單詞之間用空格分隔,不應(yīng)當(dāng)有重名。
不同點(diǎn)列于下表:
Labe1Caption
使用LabVIEW的默認(rèn)狀態(tài),即
底層VI顯示出來
Caption為空。
用戶界面隱藏顯示A多語言版本中,使用本地化語
VI多語言版本中,只使用英語
顯示
程序接口隱藏A多語言版本中,只使用英語多語言版本中,使用本地化語言
VI不用標(biāo)注控件的默認(rèn)值在后面加一括號(hào),括號(hào)內(nèi)標(biāo)注控件
的默認(rèn)值和數(shù)據(jù)單位
在LabVIEW中實(shí)現(xiàn)VI的遞歸調(diào)用
LabVIEW中使用遞歸調(diào)用不是很方便。但是遞歸并不是編程必須程序結(jié)構(gòu),任何
需要使用遞歸調(diào)用的地方,都可以用循環(huán)結(jié)構(gòu)來代替。但是在某些情況下,使用遞
歸調(diào)用的確可以大大簡(jiǎn)化程序代碼,對(duì)縮短編程時(shí)間、提高程序可讀性都非常有幫
助,所以學(xué)習(xí)一下遞歸的實(shí)現(xiàn)方法還是有好處的。
一、為什么VI不可以被靜態(tài)的遞歸調(diào)用
LabVIEW不能通過靜態(tài)調(diào)用的方法(把子VI直接放到另一VI的程序框圖上)
來實(shí)現(xiàn)遞歸。
對(duì)于一個(gè)非可重入的subVI,在每一個(gè)時(shí)間,這個(gè)subVI這能被運(yùn)營(yíng)一次。
LabVIEW需要借此來保證多線程時(shí)的數(shù)據(jù)安全。對(duì)于被遞歸調(diào)用的代碼,是需要
在它執(zhí)行到中間的時(shí)候,就再次被調(diào)用的。所以默認(rèn)設(shè)立下的VI不能被靜態(tài)遞歸
調(diào)用。
對(duì)于被設(shè)立為可重入的VI,是可以被同時(shí)調(diào)用多次的,但也不能被靜態(tài)的遞歸調(diào)
用°
除非是通過VIServei?動(dòng)態(tài)的調(diào)用VI,否則,LabVIEW是在一個(gè)程序被調(diào)入
內(nèi)存,開始運(yùn)營(yíng)之前就為它的所有VI分派好內(nèi)存空間的,涉及數(shù)據(jù)區(qū)。假如一個(gè)
VI不是可重入的,LabVIEW會(huì)在這個(gè)VI運(yùn)營(yíng)時(shí)局部變量所在的數(shù)據(jù)區(qū)開辟在
這個(gè)VI所在的空間內(nèi);對(duì)于可重入的VI,LabVIEW把它的數(shù)據(jù)區(qū)開辟在調(diào)
用者VI上,這樣就可以保證這個(gè)可重入VI在不同的地方被同時(shí)調(diào)用時(shí)使用不同的
數(shù)據(jù)區(qū),以防止多線程運(yùn)營(yíng)時(shí)數(shù)據(jù)混亂。
因此,可重入VI雖然可以被同時(shí)多次調(diào)用,但是被調(diào)用的次數(shù)是運(yùn)營(yíng)前就擬定
的。而遞歸運(yùn)算時(shí)的調(diào)用次數(shù)是運(yùn)營(yíng)時(shí)決定的。這樣,假如是靜態(tài)調(diào)用,LabVIEW
主線沒有辦法為提前為參與遞歸的VI開辟好數(shù)據(jù)區(qū)。
二、用動(dòng)態(tài)調(diào)用方法實(shí)現(xiàn)遞歸
圖1是一個(gè)采用遞歸算法計(jì)算階乘的例子,可以點(diǎn)擊后面的連接直接下載示例
VI:^Bl.raro
圖1:運(yùn)用遞歸結(jié)構(gòu)計(jì)算階乘
正如前文說過的,所有的遞歸都可以使用循環(huán)來代替,計(jì)算階乘也可以使用循環(huán)結(jié)
構(gòu),但是這里介紹的是使用遞歸結(jié)構(gòu)的方法。由于n!=n*(n-l)!,所以我們只
要編寫一個(gè)VI實(shí)現(xiàn)功能F(n)=n*F(n-l)就可以了。
程序中,遞歸調(diào)用VI自身的結(jié)構(gòu)由三個(gè)VI動(dòng)態(tài)調(diào)用節(jié)點(diǎn)實(shí)現(xiàn):OpenVI
Reference,CallByReferenceNode,CloseReference。這三個(gè)節(jié)點(diǎn)分
別負(fù)責(zé)動(dòng)態(tài)打開一個(gè)VI(本例中就是這個(gè)VI自身),運(yùn)營(yíng)這個(gè)VI,再關(guān)閉它。
使用CallByReferenceNode需要在打開VI句柄的時(shí)候就要知道VI連線
板(ConnectorPane)的布局,因此,我們?cè)谟肙penVIReference打開VI的
時(shí)候要提供VI連線板的布局信息,在例子中就是OpenVIReference節(jié)點(diǎn)
上方的那個(gè)常量。
三、使用遞歸時(shí)的幾點(diǎn)注意事項(xiàng)
遞歸調(diào)用的退出或結(jié)束條件,本例中當(dāng)輸入數(shù)據(jù)小于1時(shí)?,就需要結(jié)束遞歸調(diào)用返
回最底層的值了。假如遞歸調(diào)用的退出條件設(shè)立不妥,也許會(huì)引起程序死循環(huán)甚至
崩潰。
LabVIEW中也可以實(shí)現(xiàn)A調(diào)用B,B又調(diào)用A這種用多個(gè)VI互相調(diào)用
的遞歸結(jié)構(gòu)。
參與遞歸調(diào)用的VI必須被設(shè)立為可重入。
動(dòng)態(tài)調(diào)用的需要把VI在運(yùn)營(yíng)時(shí)調(diào)入內(nèi)存,這個(gè)過程是比較耗時(shí)的。因此遞歸結(jié)構(gòu)
的運(yùn)營(yíng)效率遠(yuǎn)不如可實(shí)現(xiàn)相同功能的循環(huán)結(jié)構(gòu),內(nèi)存占用也會(huì)更大一些。決定使用
遞歸結(jié)構(gòu)之前要考慮到這些因素。
在LabVIEW中實(shí)現(xiàn)V工的遞歸調(diào)用
LabVIEW中使用遞歸調(diào)用不是很方便。但是遞歸并不是編程必須程序結(jié)構(gòu),任何
需要使用遞歸調(diào)用的地方,都可以用循環(huán)結(jié)構(gòu)來代替。但是在某些情況下,使用遞
歸調(diào)用的確可以大大簡(jiǎn)化程序代碼,對(duì)縮短編程時(shí)間、提高程序可讀性都非常有幫
助,所以學(xué)習(xí)一下遞歸的實(shí)現(xiàn)方法還是有好處的。
一、為什么VI不可以被靜態(tài)的遞歸調(diào)用
LabVIEW不能通過靜態(tài)調(diào)用的方法(把子VI直接放到另一VI的程序框圖
上)來實(shí)現(xiàn)遞歸。
對(duì)于一個(gè)非可重入的subVL在每一個(gè)時(shí)間,這個(gè)subV:[這能被運(yùn)營(yíng)一次。
LabVIEW需要借此來保證多線程時(shí)的數(shù)據(jù)安全。對(duì)于被遞歸調(diào)用的代碼,是需要在
它執(zhí)行到中間的時(shí)候,就再次被調(diào)用的。所以默認(rèn)設(shè)立下的VI不能被靜態(tài)遞歸
調(diào)用。
對(duì)于被設(shè)立為可重入的VI,是可以被同時(shí)調(diào)用多次的,但也不能被靜態(tài)的遞歸調(diào)
用。
除非是通過VIServer動(dòng)態(tài)的調(diào)用VI,否則,LabVIEW是在一個(gè)程序被調(diào)入內(nèi)
存,開始運(yùn)營(yíng)之前就為它的所有VI分派好內(nèi)存空間的,涉及數(shù)據(jù)區(qū)。假如一個(gè)
VI不是可重入的,LabVIEW會(huì)在這個(gè)VI運(yùn)營(yíng)時(shí)局部變量所在的數(shù)據(jù)區(qū)開辟
在這個(gè)VI所在的空間內(nèi);對(duì)于可重入的VI,LabVIEW把它的數(shù)據(jù)區(qū)開辟在調(diào)
用者VI上,這樣就可以保證這個(gè)可重入VI在不同的地方被同時(shí)調(diào)用時(shí)使用不同的
數(shù)據(jù)區(qū),以防止多線程運(yùn)營(yíng)時(shí)數(shù)據(jù)混亂。
因此,可重入VI雖然可以被同時(shí)多次調(diào)用,但是被調(diào)用的次數(shù)是運(yùn)營(yíng)前就擬定
的。而遞歸運(yùn)算時(shí)的調(diào)用次數(shù)是運(yùn)營(yíng)時(shí)決定的。這樣,假如是靜態(tài)調(diào)用,
LabVIEW主線沒有辦法為提前為參與遞歸的VI開辟好數(shù)據(jù)區(qū)。
二、用動(dòng)態(tài)調(diào)用方法實(shí)現(xiàn)遞歸
圖1是一個(gè)采用遞歸算法計(jì)算階乘的例子,可以點(diǎn)擊后面的連接直接下載示例
VI:^Bl.rar。
圖1:運(yùn)用遞歸結(jié)構(gòu)計(jì)算階乘
正如前文說過的,所有的遞歸都可以使用循環(huán)來代替,計(jì)算階乘也可以使用循環(huán)結(jié)
構(gòu),但是這里介紹的是使用遞歸結(jié)構(gòu)的方法。由于n!=n*(n—l)!,所以我們只要
編寫一個(gè)VI實(shí)現(xiàn)功能F(n)=n*F(n-l)就可以了。
程序中,遞歸調(diào)用VI自身的結(jié)構(gòu)由三個(gè)VI動(dòng)態(tài)調(diào)用節(jié)點(diǎn)實(shí)現(xiàn):OpenVI
Referenee,CallByReferenceNode,CloseReference。這三個(gè)節(jié)點(diǎn)分
別負(fù)責(zé)動(dòng)態(tài)打開一個(gè)VI(本例中就是這個(gè)VI自身),運(yùn)營(yíng)這個(gè)VI,再關(guān)
閉它。
使用Ca11ByReferenceNode需要在打開VI句柄的時(shí)候就要知道VI連線
板(ConnectorPane)的布局,因此,我們?cè)谟肙penVIReference打開
VI的時(shí)候要提供VI連線板的布局信息,在例子中就是OpenVIReference節(jié)點(diǎn)
上方的那個(gè)常量。
三、使用遞歸時(shí)的幾點(diǎn)注意事項(xiàng)
遞歸調(diào)用的退出或結(jié)束條件,本例中當(dāng)輸入數(shù)據(jù)小于1時(shí),就需要結(jié)束遞歸調(diào)用返回
最底層的值了。假如遞歸調(diào)用的退出條件設(shè)立不妥,也許會(huì)引起程序死循環(huán)甚至崩
潰。
LabVIEW中也可以實(shí)現(xiàn)A調(diào)用B,B又調(diào)用A這種用多個(gè)VI互相調(diào)用的遞
歸結(jié)構(gòu)。
參與遞歸調(diào)用的VI必須被設(shè)立為可重入。
動(dòng)態(tài)調(diào)用的需要把VI在運(yùn)營(yíng)時(shí)調(diào)入內(nèi)存,這個(gè)過程是比較耗時(shí)的。因此遞歸結(jié)構(gòu)
的運(yùn)營(yíng)效率遠(yuǎn)不如可實(shí)現(xiàn)相同功能的循環(huán)結(jié)構(gòu),內(nèi)存占用也會(huì)更大一些。決定使用
遞歸結(jié)構(gòu)之前要考慮到這些因素。
一個(gè)XContro1的實(shí)例
XControl與.ctl用戶定義控件相比,其最大的提高就在于它不僅可以定義控件
的外觀,還可以定義控件的行為。
在XControl出現(xiàn)之前,同樣可以在程序中編寫代碼,控制程序的行為。在《用
XControl實(shí)現(xiàn)面向組件的編程》一文中提到了,這種方法在程序模塊劃分上有缺
陷。假如用戶想發(fā)布一個(gè)帶有特定行為的控件也是不也許的,由于控制控件行為的
代碼,是同其它代碼混雜在一起的。
運(yùn)用XControl可以解決上面提到的問題,這里以一個(gè)例子說明一下如何運(yùn)用
XContro1實(shí)現(xiàn)一個(gè)有特定行為的控件。
Windows風(fēng)格的工具條上的按鈕有一個(gè)特點(diǎn),就是當(dāng)鼠標(biāo)移動(dòng)到按鈕上方,按鈕就
會(huì)變亮或浮起。LabVIEW中默認(rèn)的按鈕沒有這樣的特性,但是實(shí)現(xiàn)這一點(diǎn)是
很容易的。
以鼠標(biāo)移上,按鈕變亮為例:在程序中,當(dāng)按鈕的MouseEnter事件發(fā)生時(shí):
把按鈕的顏色設(shè)立為淺顏色;當(dāng)按鈕的MouseLeave事件發(fā)生時(shí),把按鈕的顏
色設(shè)立為深色即可?,F(xiàn)在把界面上的按鈕和控制顏色的代碼都封裝在一個(gè)
XControl中。這樣,其別人在使用這個(gè)XContro1時(shí),就無需修改他的代碼,
而直接獲得這種顏色變化的特性了。
一、簡(jiǎn)樸行為的XControl
一方面創(chuàng)建一個(gè)空的XControL
茂ProjectExplorer-Untitle...茂ProjectExplorer-Untitle...
圖1、2:創(chuàng)建一個(gè)新的XControl
新的XControl中有四個(gè)VI。
Data.ct1定義XC。ntrol的數(shù)據(jù)類型。比如我們要做一個(gè)按鈕,數(shù)據(jù)類型應(yīng)當(dāng)是布
爾型。假如要作一個(gè)工具條,數(shù)據(jù)類型就應(yīng)當(dāng)是布爾型數(shù)組了。
State.ctl定義XControl內(nèi)部要用到的一些數(shù)據(jù),類似于類的私有變量。我們這
個(gè)簡(jiǎn)樸的例子用不到任何變量,所以可以不去動(dòng)它。
Init.vi類似于類的構(gòu)造函數(shù)。在我們這個(gè)簡(jiǎn)樸的例子中也不需要去改變它。
Facade.vi是最重要的VI,XControl的外觀和行為都是在這個(gè)VI中定義
的。Facade,vi的界面就是XContr。1控件的外觀??刂瓶丶袨榈拇a也是放
在這個(gè)VI的程序框圖上。
我們要做的是個(gè)按鈕,所以就在Facade.vi的前面板上放一個(gè)按鈕。假如希望用
戶在使用這個(gè)XControl時(shí)可以調(diào)整它的大小,在我們這個(gè)簡(jiǎn)樸例子中,只要設(shè)
立Facade,vi窗口尺寸屬性中的“在窗口尺寸變化時(shí),按比例調(diào)整控件大小”這個(gè)選
項(xiàng)就可以了。對(duì)于復(fù)雜的XControl控件,要另寫代碼,在窗口尺寸變化后重新計(jì)算
每個(gè)控件的大小和位置。
窗口尺寸屬性設(shè)立
控制按鈕顏色的代碼也需要放在Facade,vi中:把前文提到的按鈕的Mouse
Enter和MouseLeave放在這里即可。具體實(shí)現(xiàn)方法,可以參考文章結(jié)尾給出
的范例程序:氈().zipo
二、有連續(xù)運(yùn)動(dòng)的XControl
Facade.vi不可以連續(xù)運(yùn)營(yíng),只有在有事件發(fā)生時(shí),LabVIEW才會(huì)調(diào)用這個(gè)VI。
解決完這個(gè)事件,F(xiàn)acade.vi就會(huì)停止運(yùn)營(yíng)。不要試圖讓Facade,vi連續(xù)運(yùn)營(yíng),
否則會(huì)導(dǎo)致整個(gè)LabVIEW被掛起。
有時(shí)候,需要控件可以循環(huán)地或者連續(xù)一段時(shí)間地作一個(gè)動(dòng)作。比如說,需要做一
個(gè)不斷閃爍的小燈??刂茻艄忾W爍的代碼就不可以放在Facade.vi中。實(shí)現(xiàn)這種
功能的一個(gè)方法是:
把定期控制小燈顏色的代碼放在一個(gè)可重入VI中,通過小燈控件的引用參考來
定期更改它的顏色屬性。在XControl的Init.vi中把這個(gè)定期VI動(dòng)態(tài)加載并以
異步方式運(yùn)營(yíng);在XCont⑹的Uninit.vi中再把這個(gè)定期VI卸載即可。
Uninit.vi不是一個(gè)必須的XContro1功能定義VI(AbilityVI),新建的
XControl沒有這個(gè)VI。可以在工程瀏覽窗口,鼠標(biāo)右擊這個(gè)XContro1來為
它添加新的功能定義VI。
劭9.zip,它只能在LabVIEW8.5下打開。
XControl是可以在VI的面板上放多個(gè)實(shí)例的,每個(gè)實(shí)例小燈的閃爍頻率也許不
同。我在這個(gè)例子里,每個(gè)XControl實(shí)例都有自己的一個(gè)專用定期VI,由于這些
VI是可重入的。定期的方法我采用的是加延時(shí)。
我做了一下測(cè)試,發(fā)現(xiàn)現(xiàn)在的XControl有個(gè)問題,就是在程序面板上放多個(gè)
XControl實(shí)例之后,定期就變得非常不準(zhǔn)確了,小燈閃爍速度明顯減慢。這也許
是XControl的bug,也許是LabVIEW延時(shí)函數(shù)的問題。解決這個(gè)問題的方法就
是使用一個(gè)定期VI控制所有的實(shí)例,當(dāng)然這樣的實(shí)現(xiàn)方法會(huì)比較麻煩一些。
下載文章中的示例程序:A錯(cuò)誤!未定義書簽。£錯(cuò)誤!未定義書簽。
LabVIEW中的泛型容器
Google網(wǎng)站里有個(gè)Google實(shí)驗(yàn)室,有不少Google的產(chǎn)品最初就是放在這個(gè)實(shí)
驗(yàn)室里的?,F(xiàn)在NI也有NI實(shí)驗(yàn)室了。NI實(shí)驗(yàn)室公布出來的項(xiàng)目一般是NI
工程師運(yùn)用額外時(shí)間做的一些調(diào)查研究。這些項(xiàng)目不是公司的正式產(chǎn)品,但是它們
的設(shè)計(jì)很有創(chuàng)新或者是比較有應(yīng)用潛力。與其讓這些項(xiàng)目被埋沒了,不如先看看用
戶對(duì)這些項(xiàng)目的反映,假如相稱一部分用戶覺得某個(gè)項(xiàng)目非常有幫助,或許它就值
得我們?yōu)槠湓鲩L(zhǎng)投資,把它作成正式產(chǎn)品了。
我這里給大家介紹其中的一個(gè)項(xiàng)目:“LabVIEWGenericContainerMa
p”。由于這個(gè)項(xiàng)目是我設(shè)計(jì)的,所以對(duì)它了解比較多一些。當(dāng)時(shí)?,我們打算提出這
個(gè)項(xiàng)目的時(shí)候,重要有兩個(gè)目的:第一是幫助用戶編寫有復(fù)雜數(shù)據(jù)結(jié)構(gòu)的應(yīng)用程序;
第二是推動(dòng)LabVIEW向通用編程語言方向做改善。
C++的程序員基本都很喜歡STL這個(gè)模板庫。程序中常會(huì)使用數(shù)組、隊(duì)列、字符
串等等數(shù)據(jù)類型和結(jié)構(gòu),假如自己設(shè)計(jì)實(shí)現(xiàn)這些數(shù)據(jù)結(jié)構(gòu)和相關(guān)的操作,是相稱花
費(fèi)精力的。好在STL實(shí)現(xiàn)了這些數(shù)據(jù)結(jié)構(gòu),和它們常用的操作方法。借用
STL提供的功能,編程時(shí)很多細(xì)節(jié)方面不需要再去考慮了,這就讓工作簡(jiǎn)化了許
多。嘗到STL甜頭的程序員,在編寫程序的時(shí)候,已經(jīng)很難離開STL了。
STL中非常重要的一個(gè)部分就是容器。容器用于存放數(shù)據(jù),程序通過調(diào)用容器的
結(jié)構(gòu)函數(shù)保存數(shù)據(jù)到容器或者訪問容器中的數(shù)據(jù)。容器也分為不同的類型,如鏈
表、隊(duì)列等。它們?cè)跀?shù)據(jù)的組織方式上,或存取方式上有所分別,以合用不同的需
求。STL中的容器和方法都是泛型的或者說是數(shù)據(jù)類型無關(guān)的,就是說這些容器
可以保存和操作任何類型的數(shù)據(jù)。
其它一些常用的編程語言,如Java、C#也都有類似的泛型容器以方便程序員使
用。
LabVIEW的重要方針是簡(jiǎn)化工程師們編寫程序的難度,以前用LabVIEW編
寫的程序大多是工業(yè)領(lǐng)域流程控制類型的。這種類型的程序用不到太復(fù)雜的數(shù)據(jù)結(jié)
構(gòu)和算法,因此,LabVIEW中對(duì)我們?cè)谟?jì)算機(jī)課程中學(xué)到的那些經(jīng)典數(shù)據(jù)結(jié)構(gòu)以及
算法的支持并不多。
但是在我自己用LabVIEW多了之后,用它比用C++要順手,任何類型的程序
都喜歡使用LabVIEW來編寫,涉及一些通常用途的程序。這時(shí)候,LabVIEW
缺少對(duì)基本數(shù)據(jù)類型支持的缺陷就格外突出了。于是我和周邊幾個(gè)同事就想到應(yīng)當(dāng)
在這些方面對(duì)LabVIEW做一些補(bǔ)充,做一些比較規(guī)范的泛型容器和算法,一方面
方便自己,也許還可以提供應(yīng)別的用戶。
由于這不是正式項(xiàng)目,我們能投入的資源很有限,不也許一開始就做得很全面。作
為開始,我們選擇了M叩容器和它最常用的幾個(gè)方法。一方面選擇M叩一是由于
它比較常用,二是其它容器中,有些在概念上和LabVIEW中已有的一些函數(shù)比
較接近,假如選則他們,也許會(huì)引起用戶的誤解。
LabVIEW中的Array操作與STL中的vector是非常相似的,功能齊全,不需要
考慮底層操作如內(nèi)存管理等。STL中的deque,queue,stack等,與
LabVEW中的“隊(duì)列”(Queue)操作比較類似。但是LabVIEW中的隊(duì)列存在
的目的不是為了作容器,而是用于在多線程程序中通訊。在“生產(chǎn)者/消費(fèi)者”程序模
式中,經(jīng)常使用隊(duì)列在不同的線程中傳遞數(shù)據(jù)或消息。由于LabVIEW中隊(duì)列操作
重要用于不同線程間的通訊,因此它的函數(shù)并沒有采用LabVIEW的重要傳參方式
-傳數(shù)據(jù),而是采用了傳引用的方式。
我們實(shí)現(xiàn)的這個(gè)Map(這個(gè)按字面翻譯比較別扭,中文也許翻譯成“字典”還比較合
理)泛型容器功能與C++STL中的Map是類似的,它重要用于程序經(jīng)常需要
按某一關(guān)鍵字查詢數(shù)據(jù)的情況。
M叩已經(jīng)涉及了編寫查詢程序時(shí)常用的操作,比如把數(shù)據(jù)放到容器中、查找一個(gè)
數(shù)據(jù)、刪除、清空容器等。
我們的LabVIEWGenericContainerMap內(nèi)部的數(shù)據(jù)是按照平衡二叉樹
的方式組織存儲(chǔ)的,它的查詢復(fù)雜度比一般線性數(shù)據(jù)結(jié)構(gòu)的要低。這樣,在數(shù)據(jù)量
很大的情況下,使用Map的程序效率明顯高于使用數(shù)組的程序。
M叩采用的是符合LabVIEW風(fēng)格的傳數(shù)據(jù)方式,把整個(gè)M叩中的數(shù)據(jù)在不同
函數(shù)間傳遞。
C++是支持泛型編程的。簡(jiǎn)樸地說,泛型編程可以這樣理解,就是程序員可以
實(shí)現(xiàn)一個(gè)方法,這個(gè)方法可以應(yīng)用在任何合法的數(shù)據(jù)類型上。比如,前面提到的
STL,它有個(gè)“比較”方法,你可以用它來比較整數(shù),也可以比較字符串,或者使用
戶定義的類的實(shí)例等等。
支持泛型編程,程序員就可一抽象出與數(shù)據(jù)類型無關(guān)的算法,從而使代碼具有更好
的可重用性。
目前,用戶還不能在LabVIEW上實(shí)現(xiàn)泛型編程。使用PolymorphicVI可
以使一個(gè)方法支持某幾種特定的數(shù)據(jù)類型,但不是任何數(shù)據(jù)類型。真正能做到數(shù)據(jù)
無關(guān)的函數(shù),比如說0g1”函數(shù),都是1^囚正亞自帶的,用戶無法寫出這樣一
個(gè)函數(shù)或VI。
我是非常想推動(dòng)LabVIEW有朝一日也實(shí)現(xiàn)泛型編程的。這
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- T-ZJCX 0045-2024 食用菌干制品
- T-ZGZS 0411-2024 T-CCPITCSC 150-2024 綠色會(huì)展廢棄物管理規(guī)范
- T-ZMDS 10024-2024 手術(shù)導(dǎo)航設(shè)備配準(zhǔn)技術(shù)要求及試驗(yàn)方法
- 2025年度高端辦公空間無償租賃合作協(xié)議
- 2025年度能源企業(yè)質(zhì)押貸款擔(dān)保合同
- 二零二五年度企業(yè)辦公用品定制化采購合同
- 醫(yī)藥公司二零二五年度員工商業(yè)秘密保密協(xié)議及保密技術(shù)支持服務(wù)協(xié)議
- 2025年度村辦公室裝修與農(nóng)村電商市場(chǎng)拓展合作合同
- 二零二五年度酒店加盟店經(jīng)營(yíng)管理合作協(xié)議
- 2025年度物流園區(qū)開發(fā)物業(yè)移交與倉儲(chǔ)物流服務(wù)協(xié)議
- 滋補(bǔ)品市場(chǎng)洞察報(bào)告
- 部編版中考?xì)v史一輪復(fù)習(xí):七年級(jí)上、下冊(cè)歷史復(fù)習(xí)課件534張
- 江蘇省無錫市惠山區(qū)2024年統(tǒng)編版小升初考試語文試卷(含答案解析)
- 五年級(jí)下冊(cè)英語作文訓(xùn)練-外研版(三起)
- 7.2.1 圓柱(課件含動(dòng)畫演示)-【中職】高一數(shù)學(xué)(高教版2021基礎(chǔ)模塊下冊(cè))
- 便利店門店運(yùn)營(yíng)手冊(cè)
- 江蘇省南通市海安中學(xué)2025屆高一下生物期末綜合測(cè)試試題含解析
- 《行政倫理學(xué)教程(第四版)》課件 第1、2章 行政倫理的基本觀念、行政倫理學(xué)的思想資源
- 拆除工程施工拆除進(jìn)度安排
- 絕緣技術(shù)監(jiān)督上崗員:廠用電設(shè)備技術(shù)監(jiān)督考試資料一
- 衛(wèi)生監(jiān)督村醫(yī)培訓(xùn)課件
評(píng)論
0/150
提交評(píng)論