之前的文章中我們介紹了如何在.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) 調試/平台適應等問題。