2010-01-12 140 views
3

我一直在用.NET 4.0中的並行庫進行討論。最近,我開發了一個定製的ORM,用於我們大型系統必須使用的一些不尋常的讀/寫操作。這允許我用屬性修飾一個對象,並通過反射找出它必須從數據庫中提取哪些列,以及必須在寫入時輸出哪些XML。.NET 4.0中的多線程和性能

因爲我設想這個包裝可以在許多項目中重用,所以我想盡可能多地擠出包裝。該庫將主要用於.NET Web應用程序。我正在使用一次性控制檯應用程序來測試框架,以查看我創建的類。

我現在已經學會了多線程附帶的開銷。多線程會導致它運行速度變慢。從閱讀的角度來看,長久以來一直這樣做的人看起來很直觀,但實際上這對我來說是違反直覺的:如何運行一個方法30次在同一時間比運行它要慢30次按順序?

我不認爲我是由多個線程必須爭奪同一個共享對象導致的問題(雖然我還不夠好,但它還沒有確定),所以我假設減速是來自產生所有這些線程和運行時間的開銷,使它們保持直線。所以:

  • 雖然我主要是作爲一個學習練習,這是悲觀嗎?對於微不足道的非IO任務,是多線程矯枉過正?我的主要目標是速度,而不是UI的響應或任何東西。
  • 在IIS中運行相同的多線程代碼會導致它加速,因爲線程池中已經創建的線程,而現在我正在使用一個控制檯應用程序,我認爲這應該是單線程的,直到我告訴它除此以外?我即將進行一些測試,但我認爲有一些基礎知識,我很想知道爲什麼這將是一種方式或其他方式。我的控制檯應用程序也運行在具有兩個內核的桌面上,而Web應用程序的服務器會有更多,所以我可能不得不使用它作爲變量。

回答

8

線程實際上並不全部同時運行。

在臺式機上,我假設你有一個雙核心CPU,(最多也許是一個四核心)。這意味着只有2/4個線程可以同時運行。

如果你已經產生了30個線程,操作系統將不得不在上述30個線程之間進行上下文切換,以保持它們全部運行。上下文切換非常昂貴,因此減速。

作爲一個基本的建議,如果你想優化計算,我會針對每個CPU 1個線程。除此之外,你並沒有做任何額外的工作,你只是在同一個CPU上交換線程。試着把你的電腦想象成內部工作人員數量有限,你不可能同時有多少工作人員同時做更多的工作。

.net 4.0並行任務庫中的一些新功能允許您執行佔線程數量可伸縮性的事情。例如,您可以創建一堆任務,並且任務並行庫將在內部找出您有多少CPU可用,並且優化創建/使用的線程數目以避免CPU過載,因此您可以創建30個任務,但是在雙核心機器上,TP庫仍然只能創建2個線程,然後排隊。很顯然,當你在一臺更大的機器上運行它時,它的尺寸會非常好。或者你可以使用類似ThreadPool.QueueUserWorkItem(...)的東西來排隊一堆任務,並且該池將自動管理多少個線程用於執行這些任務。

是的,線程創建有很多開銷,但是如果您使用.net線程池(或4.0中的並行任務庫).net將管理您的線程創建,並且實際上可能會找到它創建的線程數量少於您創建的任務數量。它將在可用線程內部交換任務。如果你真的想要控制顯式創建實際線程,你需要使用Thread類。

[某些CPU可以使用線程做出巧妙的東西,並且可以爲每個CPU運行多個線程 - 請參閱hyperthreading - 但查看您的任務管理器,如果今天的虛擬CPU數量超過4-8,桌面]

+0

加一點什麼西蒙說,線程的最佳數量是一件困難的事情找到,因爲它往往依賴於一般的系統負載和你的代碼是幹什麼的,我最好的建議是試驗高負荷,看看什麼產生最佳性能。 – Lazarus 2010-01-12 16:21:12

+0

@Lazarus。是的,我完全同意,這是非常真實的。它也可以是非常系統特定的。這是你可能想以某種方式公開爲設置的事情,所以可以根據具體情況進行調整,並且有一些很好的默認設置。 – 2010-01-12 16:42:04

1

你說的ORM,所以我認爲有一些I/O正在進行。如果是這種情況,則線程創建和上下文切換的開銷將相對不存在。

最有可能的是,您遇到了I/O爭用:如果讀取的順序不正確,它可能會比較慢(尤其是在旋轉式硬盤驅動器上,也可能在其他存儲設備上)讀取同一組數據你按順序閱讀它。因此,如果您正在執行30個數據庫查詢,那麼如果它們全部由相同的I/O設備支持且查詢不在緩存中,那麼它們可能會按順序運行得比並行運行速度快。並行運行它們可能會導致系統幾乎同時出現一堆I/O讀取請求,這可能會導致操作系統依次讀取每個請求的小部分 - 導致驅動器頭部來回跳動,浪費寶貴的毫秒。

但這只是一個猜測;如果不知道更多信息,不可能真正確定是什麼原因造成了您的放緩。

雖然與添加兩個數字相比,創建線程「非常昂貴」,但通常不會輕易過分。如果你的操作非常短(比如說毫秒),使用線程池而不是新線程將顯着節省時間。一般來說,如果你的操作很短,你應該重新考慮並行的粒度;也許你最好將計算分割成更大的塊:例如,通過相當少的工作任務來處理整個批次的較小工作項目,而不是單獨處理每個項目。

2

有這麼多的問題,它支付了解封面下發生了什麼。我強烈推薦Joe Duffy撰寫的「Windows上的並行編程」書和「實踐中的Java併發性」一書。後者在編寫多線程代碼時需要了解處理器體系結構。有一個問題會影響你的代碼,那就是緩存,或者更可能是缺少它。

如前所述,調度和運行線程存在開銷,但您可能會發現跨線程共享數據時存在較大開銷。這些數據可能會從處理器緩存刷新到主內存中,這會導致代碼嚴重下降。

這是一種低級別的東西,託管環境應該保護我們,但是,在編寫高度並行代碼時,這正是您必須處理的問題。

我的一位同事錄製了關於Parallel.For和Parallel的性能問題的截屏視頻。的ForEach這可以幫助:

http://rocksolidknowledge.com/ScreenCasts.mvc/Watch?video=ParallelLoops.wmv