ASP.NET多線程技術(shù)解析
前幾天遇到了一個(gè)問題,我在頁面邏輯里需要調(diào)用一個(gè)webservice,處理一個(gè)比較耗時(shí)的操作,但是我不需要知道其返回值。于是我希望ASP.NET能像winform一樣使用自動(dòng)生成的webservice異步方法,通過ASP.NET多線程來解決這一問題。
你是不是想說:在頁面調(diào)用webservice的時(shí)候,直接調(diào)用其異步實(shí)現(xiàn)不就完了嗎?
這其實(shí)是行不通的,為了實(shí)現(xiàn)異步調(diào)用,我們需要對(duì)頁面進(jìn)行小小的改動(dòng),在Page元素里加上Async=true
我們很快就會(huì)發(fā)現(xiàn)這樣做的問題:
讓我們測(cè)試一下吧,現(xiàn)在我們?cè)谝粋€(gè)webservice的Helloworld方法中放入一個(gè)Thread。Sleep(10000),然后調(diào)用他的異步實(shí)現(xiàn)。通過調(diào)試,我們可以發(fā)現(xiàn)雖然程序運(yùn)行至HelloworldAsync時(shí),非常快速的返回并往下運(yùn)行,但是當(dāng)所有邏輯處理完成后,頁面并不Response,而是硬生生等待我們的線程睡醒了才返回。
可是如果我希望真正做到調(diào)了不管怎么辦呢?
你可以使用ASP.NET多線程中的Thread,或者ThreadPool,自己來啟動(dòng)一個(gè)線程,我推薦使用ThreadPool,這樣的話,這些線程都會(huì)被iis的線程池管理起來,不會(huì)造成崩潰
我們來分析一下ASP.NET多線程這兩種模式的運(yùn)用有什么特點(diǎn)
WebService自帶的異步模式為下圖的模式
這種模式適合無返回的情況,這種情況下,對(duì)子線程的調(diào)用應(yīng)該越晚越好,我們可以看到,主、子線程共存的時(shí)間越短,我們的稀缺資源線程就越安全,請(qǐng)注意的是,也許總的執(zhí)行時(shí)間不會(huì)比同步的情況更少,但是我們很快就返回了用戶界面,所以用戶體驗(yàn)?zāi)軌虻玫教岣?
使用web多線程的缺點(diǎn) :
看了上面的敘述,你也許會(huì)說,那干脆把我所有的調(diào)用都改成異步調(diào)用吧,你盡管去做吧,絕對(duì)是一場(chǎng)災(zāi)難,因?yàn)樵诋惒降耐瑫r(shí),一定一會(huì)產(chǎn)生一個(gè)新的線程等待調(diào)用的返回,即使你調(diào)用函數(shù)的返回值為void,所以異步調(diào)用的負(fù)面效果將是會(huì)產(chǎn)生許多子線程,所以注意當(dāng)你的調(diào)用非常耗時(shí),這個(gè)子線程也將長(zhǎng)期占用你的線程池,如果這樣的調(diào)用大量出現(xiàn),照樣會(huì)消耗掉所有的可用線程
那么什么情況下適合在web上使用哪種ASP.NET多線程模式呢
我們來看看這段偽代碼,他的用途是提交一個(gè)報(bào)告,方法傳入一個(gè)報(bào)告,并從一個(gè)WebService中獲得一些報(bào)告的內(nèi)容,接著插入數(shù)據(jù)庫(kù),然后在文件服務(wù)器上生成一個(gè)報(bào)告文件,最后發(fā)出一個(gè)通知,讓我們逐條命令的過一下這個(gè)方法,看看什么地方適合改為異步調(diào)用?(記得我們的討論都是基于web的,關(guān)于桌面運(yùn)用的多線程請(qǐng)參考 多線程總結(jié)一)
- public void CreateReport(Report report){
- //從webservice上取得報(bào)告的一些信息,
- 不取得這些信息報(bào)告,報(bào)告是不完整的,是不能提交的
- Report fullreport=CallWebService(report);
- //插入數(shù)據(jù)庫(kù),很重要的工作
- InsertIntoDataBase(fullreport)
- try{
- //生成報(bào)告文件,這里是一個(gè)耗時(shí)而且容易出錯(cuò)的操作
- WriteStaticFile(fullreport)
- }
- catch{//記錄錯(cuò)誤日志。。。。}
- //這個(gè)只是通知郵件
- CallMailService2(fullreport)
- }
第一條語句CallWebService()從一個(gè)webservice里加載一些報(bào)告的內(nèi)容,這個(gè)是業(yè)務(wù)邏輯相關(guān)的,因?yàn)槿绻患虞d的話報(bào)告內(nèi)容是不完整的,不能提交,顯然不能改為異步調(diào)了不管的模式,在這里你可以嘗試模式一,但是這個(gè)改動(dòng)是沒有作用的,因?yàn)槠渌械倪^程,包括插入數(shù)據(jù)庫(kù),生成報(bào)告都依賴于這個(gè)方法的返回,所以如果我們?cè)谶@里使用異步的話,其他的所有操作都必須等待他的返回,所以采用異步除了多增加了線程以外,一點(diǎn)時(shí)間也不能節(jié)省
再來看插入數(shù)據(jù)庫(kù),和上面一樣也沒有必要使用異步調(diào)用
生成報(bào)告這里比較有趣,確實(shí)他是一個(gè)和邏輯息息相關(guān)的操作,但是通過分析代碼,我們可以看出,雖然報(bào)告生成是一個(gè)重要業(yè)務(wù)步驟,但是并沒有嚴(yán)格到說"如果不能生成報(bào)告,就必須回滾上面的操作",并且如果操作失敗,在catch中也僅僅是記錄了日志,并沒有需要嘗試重寫的邏輯,(很有可能另外的某個(gè)程序或者某人,會(huì)定時(shí)查看日志,發(fā)現(xiàn)有錯(cuò)誤就重新生成文件)也就是說,就這段代碼而言,生成也可以算一個(gè)額外邏輯,那么自然也可以去異步操作.可是:千萬注意!!
由于生成報(bào)告需要的時(shí)間較長(zhǎng),那么生成報(bào)告的子線程會(huì)長(zhǎng)時(shí)間運(yùn)行,長(zhǎng)期無法返回線程池,如果請(qǐng)求量太大,頻率太快,那就會(huì)耗盡線程資源了.
平心而論,這個(gè)問題其實(shí)不是異步造成的,即使時(shí)同步調(diào)用,執(zhí)行此操作也需要化肥很長(zhǎng)時(shí)間,調(diào)用量太大,頻率太快,也會(huì)造成排隊(duì).而且由于返回時(shí)間太長(zhǎng),用戶體驗(yàn)也不會(huì)好,所以我們的這個(gè)ASP.NET多線程的改造應(yīng)該是有益的。
【編輯推薦】