前幾天遇到了一個問題,我在頁面邏輯里需要調用一個webservice,處理一個比較耗時的操作,但是我不需要知道其返回值。于是我希望asp.net能像winform一樣使用自動生成的webservice異步方法
你是不是想說:在頁面調用webservice的時候,直接調用其異步實現不就完了嗎?
這其實是行不通的,為了實現異步調用,我們需要對頁面進行小小的改動,在page元素里加上async=true
我們很快就會發現這樣做的問題:
讓我們測試一下吧,現在我們在一個webservice的helloworld方法中放入一個thread。sleep(10000),然后調用他的異步實現。通過調試,我們可以發現雖然程序運行至helloworldasync時,非常快速的返回并往下運行,但是當所有邏輯處理完成后,頁面并不response,而是硬生生等待我們的線程睡醒了才返回。
可是如果我希望真正做到調了不管怎么辦呢?

web中使用多線程來增強用戶體驗
你可以使用thread,或者threadpool,自己來啟動一個線程,我推薦使用threadpool,這樣的話,這些線程都會被iis的線程池管理起來,不會造成崩潰
我們來分析一下這兩種模式的運用有什么特點
webservice自帶的異步模式為下圖的模式

web中使用多線程來增強用戶體驗
主線程調用子線程執行一個耗時操作(work1),同時執行一系列同步操作(w2...w5),然后交給w1返回
這種模式適合于work1有返回的情況,并且為了讓work1得到充分的工作時間,異步調用的過程開始的越早越好,對web程序設計者而言,這里有一個很重要的問題:線程占用。。
剛才我們談過,asp.net中每個請求都會有有一個線程來處理,而可以使用的線程是有限的,服務器會使用一個線程池來管理線程,當線程耗盡,ok,新來的請求只能蹲著排隊,所以對web開發者而言,線程是個寶貴的資源,所以這個方案在并行處理的同時也增加了耗盡線程池的風險,畢竟一個請求造成了多個線程
用線程池來實現的模式屬于下圖

web中使用多線程來增強用戶體驗
這種模式適合無返回的情況,這種情況下,對子線程的調用應該越晚越好,我們可以看到,主、子線程共存的時間越短,我們的稀缺資源線程就越安全,請注意的是,也許總的執行時間不會比同步的情況更少,但是我們很快就返回了用戶界面,所以用戶體驗能夠得到提高
使用web多線程的缺點 :
看了上面的敘述,你也許會說,那干脆把我所有的調用都改成異步調用吧,你盡管去做吧,絕對是一場災難,因為在異步的同時,一定一會產生一個新的線程等待調用的返回,即使你調用函數的返回值為void,所以異步調用的負面效果將是會產生許多子線程,所以注意當你的調用非常耗時,這個子線程也將長期占用你的線程池,如果這樣的調用大量出現,照樣會消耗掉所有的可用線程
那么什么情況下適合在web上使用哪種多線程模式呢
我們來看看這段偽代碼,他的用途是提交一個報告,方法傳入一個報告,并從一個webservice中獲得一些報告的內容,接著插入數據庫,然后在文件服務器上生成一個報告文件,最后發出一個通知,讓我們逐條命令的過一下這個方法,看看什么地方適合改為異步調用?(記得我們的討論都是基于web的,關于桌面運用的多線程請參考 多線程總結一)
第一條語句callwebservice()從一個webservice里加載一些報告的內容,這個是業務邏輯相關的,因為如果不加載的話報告內容是不完整的,不能提交,顯然不能改為異步調了不管的模式,在這里你可以嘗試模式一,但是這個改動是沒有作用的,因為其他所有的過程,包括插入數據庫,生成報告都依賴于這個方法的返回,所以如果我們在這里使用異步的話,其他的所有操作都必須等待他的返回,所以采用異步除了多增加了線程以外,一點時間也不能節省
再來看插入數據庫,和上面一樣也沒有必要使用異步調用
生成報告這里比較有趣,確實他是一個和邏輯息息相關的操作,但是通過分析代碼,我們可以看出,雖然報告生成是一個重要業務步驟,但是并沒有嚴格到說"如果不能生成報告,就必須回滾上面的操作",并且如果操作失敗,在catch中也僅僅是記錄了日志,并沒有需要嘗試重寫的邏輯,(很有可能另外的某個程序或者某人,會定時查看日志,發現有錯誤就重新生成文件)也就是說,就這段代碼而言,生成也可以算一個額外邏輯,那么自然也可以去異步操作.可是:千萬注意!!
由于生成報告需要的時間較長,那么生成報告的子線程會長時間運行,長期無法返回線程池,如果請求量太大,頻率太快,那就會耗盡線程資源了.
平心而論,這個問題其實不是異步造成的,即使時同步調用,執行此操作也需要化肥很長時間,調用量太大,頻率太快,也會造成排隊.而且由于返回時間太長,用戶體驗也不會好,所以我們的這個改造應該是有益的.
新聞熱點
疑難解答