淺談.NET下的多執行緒和平行計算(十四)平行計算前言

隨緣主人發表於2010-05-19

之前的文章中我們介紹瞭如何在.NET下運用相關類庫進行多執行緒程式設計的基礎,我們知道.NET 4.0已經正式推出了,帶來的重要特性是並行庫。本文就談談對平行計算的一些理解和看法。平行計算不是一個很新的概念,其實它就是通過多執行緒把同一個任務分割成多個子任務並行的執行的過程。.NET 4.0並行庫不但提供了這方面的支援,而且還封裝了多執行緒開發的各種場景,使得我們不需要依賴Thread/同步基元等“底層”的物件就可以進行多執行緒開發。沒有.NET 4.0的平行計算庫我們同樣可以進行平行計算,只不過我們需要手動分割任務所依賴的資料結構和演算法,放到不同的執行緒中去,然後再使用執行緒同步的方法來統一彙總和處理這些執行緒的執行結果。之所以會需要平行計算是因為隨著我們電腦的CPU不在是升級頻率而是橫向擴充套件核心,我們的程式也就達不到隨著CPU的Scale-out而Scale-up的能力,因此,需要調整程式的邏輯使得一段原本不分割的任務也分割成多個片段同時執行,這樣就可以利用到多個核心從而提供程式的效能。

微軟每一次推出一個新的框架都會有很多宣傳,但我們並沒有從這些宣傳中看到一些建議,在什麼時候我們不應該去使用這個框架。在Linq to sql推出之後,很多人開始使用並放棄了儲存過程,但在使用的時候毫不關心背後框架做了什麼,因為微軟的東西實在是太容易使用了,不瞭解ORM的人可以很方便的學會使用Linq to sql:

1) 隨便把資料庫訪問包在迴圈中,或者是GridView類似控制元件的ItemDataBound類似迴圈觸發事件的方法中進行資料庫訪問操作,導致一個頁面上百個上千個資料庫連線,在以前使用儲存過程的時候誰也不會這麼幹。

2) 即使讀取一個欄位一條記錄也把所有欄位,以及行所關聯的字表的數千行記錄都取出來。

這就導致Linq to sql變成一個危險的產品,因為在沒有使用前,古老的方式讓大家都不會犯錯誤,一旦變成自動化了,大家就很容易犯錯誤。其實我想說的是.NET 4.0的並行框架也可能會這樣,為什麼這樣說呢? 其實,對於一個Windows應用程式的程式設計師來說,即使沒有.NET 4.0並行庫,他們也非常清楚怎麼去使用執行緒,進行多執行緒的程式設計,.NET 4.0並行庫的到來只是簡化了多執行緒程式設計方式,以及讓我們更容易進行平行計算。我想說的是,如果程式需要優化需要利用多核的話,在並行庫到來之前他也就這麼幹了。但對於ASP.NET程式設計師來說就不一樣了,很多ASP.NET程式設計師在.NET 4.0並行庫或平行計算這個詞出現之前沒接觸過多執行緒開發,也很少在網站中直接去使用執行緒,看到.NET 4.0並行庫之後很容易以為這是提高效能的一個良方。而且也確實很容易使用,我們只要加幾行程式碼就可以讓我們的迴圈遍歷變成一個並行的行為,就可以讓我們的同一個方法內的不同程式碼段在多個執行緒中並行執行。

其實,對於ASP.NET程式來說這種做法不一定能提高效能,可能會降低效能,甚至會導致網站癱瘓,很可能會造成很多莫名其妙的BUG。主要原因有兩點:

1) 之前沒有多執行緒背景,由於太容易使用了,盲目把程式改造成並行程式,但看不到其中潛在的問題。

2) WEB程式本身就是處於一個多執行緒環境中的,和Windows程式不一樣,我們的使用者不是一個,我們的程式已經由WEB伺服器的執行緒池中的若干執行緒同時執行了。換一句話說,WEB應用程式即使一個頁面從頭到尾從處理UI到讀取資料到格式化UI只是一個執行緒的話,我們同樣能充分利用CPU資源,利用到多核,因為作業系統會把不同的執行緒排程到不同的核上去。

現在就這兩點問題進行一下展開,首先是平行計算(多執行緒)會有哪些潛在問題?

1) 多個執行緒共享相同的記憶體分配,很典型的對值進行累加,如果多個執行緒同時訪問的話累加還準確嗎?

2) 過多的執行緒,即使不存在共享記憶體,對於一百個一個短時間的操作使用一百個新執行緒執行的話最終執行的時間很可能大於在一個執行緒中迴圈順序執行這個操作一百次。

3) 即使我們自己的程式是執行緒安全的,在多執行緒環境下呼叫.NET類庫或其它類庫的方法是不是執行緒安全的?即使是執行緒安全的,我們還要意識到一點,如果它本來就採用鎖方式確保執行緒安全的話即使我們多執行緒去呼叫這個方法也不能帶來效能的提升。

4) 多執行緒操作UI的問題。

5) 互相等待或死鎖的問題。

6) 除錯/平臺適應等問題。

第二個問題是,ASP.NET 應用程式或說WEB應用程式是否應該使用並行庫的問題?

1) 如果我們的操作涉及到IO(磁碟/資料庫/網路),操作的時間長並且可以分割,那麼我們可以使用多個執行緒來進行一個大操作,或讓多個操作同時執行,加快主執行緒完成操作的時間。加快時間不但能帶來使用者體驗上的改善,而且能提高系統吞吐,可以想一下,同樣的負載,執行緒池100個工作執行緒(等IO),100個IO執行緒好一點還是25個工作執行緒,100個IO執行緒好一點?

2) 如果我們的操作很多是CPU繫結的那麼要看情況了,如果連線數很高,並且我們的操作都是很短的話,使用並行效果並不會很好,因為我們的CPU沒閒著,已經在不斷處理任務,執行緒池也開啟了任何執行緒,再把一個執行緒能做的事情分成幾個執行緒去做,加大了執行緒切換的負擔,加大了執行緒池需要使用的執行緒,加大了系統負擔也就減少了吞吐。只有在我們的程式比較複雜並且連線很少(比如是內部的ERP),甚至類似Windows程式的情況下,使用並行才會有好處,增加CPU利用率,提高執行速度。

3) 還要注意一點,我們在開發類庫的時候如果知道類庫將來的使用群體可能是ASP.NET應用程式,那麼我們要慎重考慮是不是需要在類庫中引入大量的並行度非常高的操作。

4) 有的時候還是要使用測試資料做依據,不能想當然,比如我們可以想到使用多執行緒同時下載10個網站的資料會提高效率,那麼我們也會理所當然認為使用10個執行緒訪問資料庫更新第i*100(i=0-9)條資料會比序列更新1000條資料快一點。但是我們清楚資料庫在背後會有什麼鎖操作嗎?

我想說的是,在請求很少甚至只有一兩個請求的情況下確實可能會提高速度,但是請求多了誰說的清楚呢,比如在一條不寬的傳送帶上並行傳送我們的行李,分成四堆(四道),如果只有一個人的行李需要傳送可能是快一點。但是如果是一千人呢,每個人的行李都鋪開傳還是每個人的行李都只佔用一條道快?難說!對於大量訪問的ASP.NET應用程式來說還是慎重點吧,我們一個請求只用一個工作執行緒一個IO執行緒夠了。

 

不管怎麼說,.NET 4.0的並行庫給我們提供了工具,怎麼去用好它合理使用,還需要根據需求來判斷。況且並行庫除了之前說了資料並行,任務並行之外,還提供了很多適合多執行緒環境的集合資料結構(在這之前要實現一個Lock-Free效能優良的適合多執行緒的資料結構也不是這麼簡單的事情)。

 

最後引申一下,平行計算是否能不侷限於使用CPU,使用GPU呢?http://www.infoq.com/cn/news/2010/05/Brahma

可否把整個方法指令棧和資料棧作為引數傳遞給多個伺服器,讓伺服器處理完之後返回結果呢?

作者:lovecindywang
本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段宣告,且在文章頁面明顯位置給出原文連線,否則保留追究法律責任的權利。


相關文章