国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > .NET > 正文

VB.NET中使用代表對(duì)方法異步調(diào)用

2024-07-10 13:02:37
字體:
供稿:網(wǎng)友

最大的網(wǎng)站源碼資源下載站,

按照我們常規(guī)的思維方式,計(jì)算機(jī)應(yīng)該是干完一件事,然后再干下一件。用術(shù)語來說,這種執(zhí)行任務(wù)的方式叫做同步執(zhí)行(synchronous execution)。既然這樣,那么為什么要引入異步執(zhí)行的概念呢?

目錄

為什么要使用異步調(diào)用
實(shí)現(xiàn)異步調(diào)用的步驟和機(jī)理

為什么要使用異步調(diào)用(asynchronous method execution)

按照我們常規(guī)的思維方式,計(jì)算機(jī)應(yīng)該是干完一件事,然后再干下一件。用術(shù)語來說,這種執(zhí)行任務(wù)的方式叫做同步執(zhí)行(synchronous execution)。既然這樣,那么為什么要引入異步執(zhí)行的概念呢?原因很簡單,因?yàn)橥綀?zhí)行在有些情況下效果不理想,不能完成我們預(yù)期的目的。舉兩個(gè)簡單的例子來說明一下這個(gè)問題。

a. 一個(gè)客戶端程序(client side program)要從后臺(tái)數(shù)據(jù)庫取回一個(gè)復(fù)雜的數(shù)據(jù)集合。可能這個(gè)數(shù)據(jù)庫操作本身很費(fèi)時(shí),也可能是網(wǎng)絡(luò)傳輸?shù)臄?shù)度比較慢,總之這個(gè)方法調(diào)用可能要花20秒時(shí)間。如果使用同步調(diào)用,那么在數(shù)據(jù)庫結(jié)果返回之前,用戶必須耐心等待,什么也不能做。這時(shí)候你可能會(huì)希望這個(gè)調(diào)用慢慢的在別處進(jìn)行,程序馬上返回好讓你做其它的工作。等什么時(shí)候數(shù)據(jù)返回了,在進(jìn)行其隨后相應(yīng)的操作。這種情形下,你就需要對(duì)數(shù)據(jù)庫操作的方法進(jìn)行異步調(diào)用。

b.一個(gè)網(wǎng)上機(jī)票查詢訂閱程序。當(dāng)客戶要查詢從北京到芝加哥的所有機(jī)票的時(shí)候,這個(gè)程序可能要在后臺(tái)通過web service對(duì)美國西北航空公司,中國國際航空公司和東方航空公司進(jìn)行訪問。將這些公司的機(jī)票情況匯總后一起以html的形式返回給用戶。如果是同步調(diào)用,那么需要一個(gè)接一個(gè)進(jìn)行web service調(diào)用。如果每個(gè)調(diào)用花費(fèi)10秒鐘的話,那么整個(gè)過程就要30秒鐘。如果你使用異步調(diào)用,那么你可以在同幾乎一時(shí)間就對(duì)三個(gè)公司發(fā)出相應(yīng)的請(qǐng)求,10秒后當(dāng)結(jié)果從三個(gè)不同的網(wǎng)站返回來后,你就可以匯總并返回各用戶了。這樣,整個(gè)過程只需要10秒左右。

看到這里你可能會(huì)說,這個(gè)問題沒什么新鮮的。我在c++,java里都可以用線程(thread)來達(dá)到這樣的效果。的確,大多數(shù)的高級(jí)語言都允許你創(chuàng)建新的線程來手工實(shí)現(xiàn)這樣的調(diào)用。但是這些手工操作比較復(fù)雜,程序員需要自己控制線程的創(chuàng)建,銷毀,協(xié)調(diào)等等許多細(xì)節(jié)工作,容易產(chǎn)生錯(cuò)誤。并且在大型的服務(wù)器端的程序中,手工控制線程有時(shí)性能不夠優(yōu)化,不能根據(jù)當(dāng)前具體服務(wù)器的處理器情況來動(dòng)態(tài)的和智能的優(yōu)化線程的數(shù)量。基于這個(gè)原因,.net創(chuàng)建了一種相對(duì)簡單的異步方法調(diào)用機(jī)制,使這一問題變得更加簡單。這就是今天要談的使用代表(delegates)對(duì)方法進(jìn)行異步調(diào)用。(本文以vb.net來進(jìn)行示范,c#的異步調(diào)用和此類似,就不再給出例程了)

實(shí)現(xiàn)異步調(diào)用的步驟和機(jī)理

假設(shè)有這樣一個(gè)方法(method),它接受一個(gè)班級(jí)的名稱,然后查詢數(shù)據(jù)庫,返回這個(gè)班級(jí)所有同學(xué)的名單。

class democlass
public shared function getstudentslist(classname as string)
as string()
'查詢數(shù)據(jù)庫
'其它操作
end function
end class



如果對(duì)這樣一個(gè)方法進(jìn)行異步調(diào)用的話,那么你首先需要定義一個(gè)有同樣方法簽名(function signature)的代表(delegate),比如

delegate function getstudentlistdelegate (classname as string) as string()



下一步,你需要生成一個(gè)代表實(shí)例(instance),然后將這個(gè)代表和你的真正的方法“捆綁”起來,如

dim delegate as getstudentlistdelegate
getstudentlistdelegate = addressof democlass.getstudentslist



(為了簡單起見,這里使用了靜態(tài)方法,這其實(shí)不是必須的)

當(dāng)你做到這步的時(shí)候,.net的編譯器在后臺(tái)為你的代表增加了幾個(gè)方法,它們是invoke, begininvoke, endinvoke.

如果你使用invoke方法,那么其效果是同步調(diào)用,比如

delegate.invoke("class90")



在這種情況下,代表將輸入?yún)?shù)"class90"傳遞給方法getstudentslist,然后將這個(gè)方法的返回值返回給用戶。這種使用方法是同步的,不是我們所期待的。如果要達(dá)到異步效果,我們要使用begininvoke和endinvoke。

讓我們先看看begininvoke

你的使用方法可能如下所示:

dim ar as system. iasyncresult
ar = delegate.begininvoke("class90",nothing, nothing)



你可能會(huì)發(fā)現(xiàn),這種調(diào)用方法有些不同。首先是多出兩個(gè)輸入?yún)?shù),其次是返回值是system. iasyncresult。這到底是怎么一回事呢?

當(dāng)你調(diào)用begininvoke的時(shí)候,一系列的事情在后臺(tái)自動(dòng)發(fā)生了。


當(dāng)你用代表發(fā)出調(diào)用請(qǐng)求后,clr(公共語言運(yùn)行環(huán)境,common language runtime)接到這個(gè)請(qǐng)求,并將這個(gè)請(qǐng)求放置到一個(gè)內(nèi)部的處理隊(duì)列(queue)中去。一旦放置完成后,clr馬上就給調(diào)用者返回一個(gè)iasyncresult的對(duì)象。這個(gè)對(duì)象很重要,我們一會(huì)兒還要解釋他的具體作用。


當(dāng)調(diào)用者收到返回的iasyncresult對(duì)象后,它就可以進(jìn)行下一步的工作。由于將請(qǐng)求放置到隊(duì)列中是個(gè)非常快速的操作,所以調(diào)用者馬上就可以去完成下一個(gè)動(dòng)作,沒有被“阻擋(block)”。


clr在后臺(tái)維持著一個(gè)“線程池(thread pool)”。這些線程守候著前面提到的那個(gè)處理隊(duì)列。一旦有任務(wù)被放置到隊(duì)列中,一個(gè)線程就會(huì)拿到這個(gè)任務(wù)并執(zhí)行它。也就是說原來要調(diào)用者線程執(zhí)行的費(fèi)時(shí)的操作被線程池中的一個(gè)線程代勞了。(這里你可以看出,不管是用什么樣的語言,在異步調(diào)用中,一定有其它的線程出現(xiàn)。或者是你手工創(chuàng)建它(如java),或者是系統(tǒng)為你創(chuàng)建(如.net)。那么這個(gè)“線程池”中究竟有幾個(gè)線程呢?這個(gè)問題你可以不用關(guān)心。clr會(huì)根據(jù)程序的特點(diǎn)以及當(dāng)前的硬件條件自行決定。比如對(duì)于運(yùn)行在單處理器平臺(tái)上的一般的桌面程序,這個(gè)線程池可能有幾個(gè)線程;而對(duì)于一個(gè)運(yùn)行在4處理器服務(wù)器上的后臺(tái)應(yīng)用,線程池可能會(huì)有近百個(gè)線程。這樣做的好處就是降低程序員的開發(fā)難度,讓.net的clr去解決這些和用戶應(yīng)用邏輯無關(guān)的問題。)

既然有線程池的線程代替完成了那個(gè)方法調(diào)用(getstudentslist),那么我們怎么知道后臺(tái)的這個(gè)調(diào)用什么時(shí)候完成呢?這個(gè)方法調(diào)用返回的值(這里是一串學(xué)生名單)我們怎么拿到呢?這里我們就要用到前面提到的那個(gè)返回的iasyncresult對(duì)象了。

這個(gè)iasyncresult對(duì)象一個(gè)“收據(jù)”似的,通過它你可以查詢后臺(tái)調(diào)用是否完成。如果已經(jīng)完成,你可以通過它來取回你想要的結(jié)果。

dim ar as system.iasyncresult
ar = delegate.begininvoke("class90",nothing, nothing)
'*** 其它一些操作
。。。
'*** 檢查后臺(tái)調(diào)用狀態(tài)
if (ar.iscompleted) then
'*** 取回異步調(diào)用方法的結(jié)果
end if



如果后臺(tái)調(diào)用已經(jīng)結(jié)束,那么你就可以用代表的endinvoke來得到返回值。

dim students as string()
students = delegate.endinvoke(ar)



那么,如果你沒有測試后臺(tái)調(diào)用是否結(jié)束而直接使用endinvoke,那后果會(huì)怎么樣呢?如果后臺(tái)調(diào)用沒有完成,endinvoke調(diào)用就會(huì)被“阻擋”,直到后臺(tái)調(diào)用完成后才返回。如果后臺(tái)調(diào)用出現(xiàn)異常,那么endinvoke還可以捕捉到這個(gè)異常

dim students as string()
trystudents = delegate.endinvoke(ar)
catch ex as exception
'處理這個(gè)異常
end try



既然endinvoke調(diào)用就會(huì)被“阻擋”(如果后臺(tái)異步調(diào)用還沒有完成),那么下面這種標(biāo)較復(fù)雜情況clr是怎樣處理的呢?

dim ar1, ar2 as system.iasyncresult
dim rt1, rt2 as string()
ar1 = delegate1.begininvoke("class90",nothing, nothing)
ar2 = delegate2.begininvoke("class94",nothing, nothing)
rt1 = delegate1.endinvoke(ar1)
rt2 = delegate2.endinvoke(ar2)



在這個(gè)例子中,delegate1的調(diào)用和delegate2的調(diào)用完成順序可能會(huì)有多種情況。比如delegate2的調(diào)用后發(fā)先至,那么endinvoke的使用順序是不是很重要呢?事實(shí)上,你可以忽略這個(gè)問題,clr會(huì)保證在兩個(gè)異步調(diào)用都結(jié)束后,你才可以進(jìn)行下面的操作。至于它是怎么實(shí)現(xiàn)的,你可以不去管它。

事實(shí)上,endinvoke是非常重要的。如果你使用了begininvoke,那你最好使用endinvoke。因?yàn)槟闳绻皇褂胑ndinvoke,那么后臺(tái)調(diào)用的異常就沒有機(jī)會(huì)被捕捉到。另外,使用了endinvoke可以讓clr釋放異步調(diào)用中所使用的資源,否則你的應(yīng)用程序就可能出現(xiàn)資源泄漏(resource leak)。

到這里,情況已經(jīng)比較清楚了。使用delegate可以讓后臺(tái)線程代替當(dāng)前線程去完成費(fèi)時(shí)的操作,從而使當(dāng)前線程不被“阻擋”,可以馬上進(jìn)行其它的工作。但是,如果當(dāng)前線程通過endinvoke來得到異步調(diào)用的結(jié)果,它又很可能被“阻擋”。看起來有點(diǎn)“拆了東墻補(bǔ)西墻”的樣子,好像我們沒有得到什么好處。打個(gè)比方來說吧,你要到復(fù)印室去復(fù)印一批材料,這個(gè)工作要費(fèi)時(shí)一個(gè)多小時(shí)。同步調(diào)用就意味著你自己親自去復(fù)印,一個(gè)多小時(shí)候再返回辦公室作其它工作。異步調(diào)用意味著你可以把復(fù)印材料交到復(fù)印室,那里有專人負(fù)責(zé)復(fù)印。你放下材料后就可以回到辦公室去干其它工作了。但問題是,你要不停的查看材料是否復(fù)印好了,一旦發(fā)現(xiàn)復(fù)印完畢后,就馬上取回作相應(yīng)的操作。你不停的查看(調(diào)用代表的iscomplete方法)或者是“干等”(調(diào)用代表的endinvoke方法)實(shí)際上還是把你“捆住”了,你沒有能騰出手來干其它的事。能不能我把材料放到復(fù)印室就不管了,等復(fù)印好后他們給我把材料送回來?。答案是可以的,那就是利用回調(diào)函數(shù)(callback function)。

還記得我們前面的那個(gè)例子嗎,我們用代表調(diào)用begininvoke的時(shí)候,多了兩個(gè)參數(shù),其中一個(gè)就是回調(diào)函數(shù),另外一個(gè)是執(zhí)行回調(diào)函數(shù)的參數(shù)。回調(diào)函數(shù)的意思是在后臺(tái)線執(zhí)行完異步調(diào)用的方法后,自動(dòng)去執(zhí)行的函數(shù)(或方法)。在執(zhí)行這個(gè)回調(diào)函數(shù)的時(shí)候,你還可以指定參數(shù)。也是就說,你讓復(fù)印室的復(fù)印員完成復(fù)印后,把材料給你放回到你的辦公桌上,并且每10頁一摞。這個(gè)“放到辦公桌上”就是回調(diào)函數(shù),而“每10頁一摞”就是回調(diào)函數(shù)執(zhí)行時(shí)使用的參數(shù)。

'回調(diào)函數(shù)的參數(shù)
dim myvalue as integer = 10
'回調(diào)函數(shù)的定義
sub puttodesk(byval ar as iasyncresult)
dim x as integer = cint(ar.asyncstate)'拿到參數(shù)
'相應(yīng)的操作
end sub
'使用回調(diào)函數(shù)的方法
private callbackdelegate as asynccallback = addressof puttodesk
...
dim ar as system.iasyncresult
ar = delegate.begininvoke("class90",callbackdelegate, myvalue)



在使用回調(diào)函數(shù)時(shí)要注意,你的回調(diào)函數(shù)必須和.net系統(tǒng)定義的asynccallback一起使用,即你的回調(diào)函數(shù)必須和asynccallback具有一樣的簽名。也就是說它必須是子程序(sub procedure),必須有一個(gè)iasyncresult類對(duì)象為輸入?yún)?shù)。

要注意的是回調(diào)函數(shù)是由后臺(tái)線程來執(zhí)行的(就是我們所說的復(fù)印員)。這種執(zhí)行方法在有些情況下會(huì)造成不小的問題。比如說,在windows的桌面應(yīng)用中有這樣一個(gè)規(guī)則,那就是一切用戶界面元素的更改(外觀以及屬性)必須由這些界面元素的創(chuàng)建線程來進(jìn)行(術(shù)語上叫界面主線程,primary ui thread)。如果其它線程試圖更新界面元素,那么將會(huì)有不可預(yù)測的后果。如果你違反了這一原則,那么你的程序在理論上講是不安全的,即使是問題你一時(shí)還沒有發(fā)現(xiàn)。

就上面一個(gè)例子而言,如果后臺(tái)線程從數(shù)據(jù)庫里拿到了學(xué)生名單,那么很可能它要執(zhí)行的回調(diào)函數(shù)就是更新界面上的一個(gè)下拉式列表(dropdown list),或是一個(gè)表格(grid)什么的。但是這樣做又違反了我們所說的界面更新的線程原則。那么我們該怎么辦呢?

其實(shí)這個(gè)問題并不難解決,設(shè)計(jì)師在設(shè)計(jì).net的時(shí)候已經(jīng)考慮到了這個(gè)問題。具體的解決辦法我將在下篇文章中做出介紹。
發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 城口县| 当阳市| 双辽市| 沁阳市| 成安县| 阿尔山市| 珠海市| 临江市| 进贤县| 五大连池市| 绥中县| 墨江| 阆中市| 建水县| 九龙坡区| 北宁市| 象山县| 大石桥市| 福泉市| 穆棱市| 南充市| 壤塘县| 沁水县| 永昌县| 廊坊市| 南宫市| 项城市| 来凤县| 呼和浩特市| 嘉定区| 双牌县| 黄梅县| 呼和浩特市| 昌邑市| 元谋县| 苍梧县| 潮安县| 泗水县| 万年县| 禹州市| 阿瓦提县|