讀書感受 – 程式員 – C#線程參考手冊(多線程技術分析)

來源:互聯網
上載者:User

      這幾天,花了些時間,瀏覽了下《C#線程參考手冊》,對初學者比較有用。。。

      該書可以在我CSDN下載頻道獲得,請購買原書支援正版(http://lzhdim.download.csdn.net/)。

      幾年前買過一本Intel的工程師寫的《多核程式設計技術》一書,本來想開個專題來對多核程式的設計做介紹的,由於時間問題,該專題改為“並行程式設計”了,但該書的重要內容卻沒有記錄下來,比較遺憾,後續有時間再補吧。(現在叫並行程式的比較多)

      其實Intel組織開展過多次並行程式的活動和編程專題,一來推廣它的多核CPU,二來對推進並行程式的設計開發做鋪墊,畢竟它和微軟也是老夥伴了,向來不是我的軟體推動你的硬體的發展,要不就是我的硬體更多的系列來支援你的軟體更新換代。

      其實對於CPU的多核的發展,我覺得是挺慢的。早在多年前,DSP的硬體就已經支援平行處理了,而且有不少的晶片系列,開發板之類的,對於那些應用早就如火如荼的開展了(當時CPU還是單核的,伺服器要裝幾個CPU,即主板上有幾個CPU插槽)。而電腦CPU的發展比較緩慢,一個是由於硬體工藝技術上的發展限制(其實也挺快了,Intel一直都是用摩爾定律來進行硬體的升級發展),主要是nm級的火拚吧;一個也是價格上的問題,畢竟新工藝在實驗室裡研究成功後,還需要一定的時間才能投入到生產中;一個也是前面的CPU系列的更新換代問題,廠商們需要時間來推廣和銷售他們對應的電腦產品,比如主板,記憶體之類(產品線的更新是個大問題);還有一個重要的,就是作業系統的支援。作業系統需要根據新的硬體升級,更好的發揮出硬體的能力,更多的榨取硬體的價值。作業系統的價值不僅僅在於配合硬體,更好的提供客戶的體驗才是最主要的。(現在GPU的發展倒是挺快,搶了CPU的風頭。CPU最初的應用就是計算,結果現在倒是大幅度的應用GPU的計算能力,真是對CPU的諷刺。)

      墨跡了這麼多,轉入正題吧。。。

      一、說到線程,從硬體CPU開始。早期的CPU技術,單核的,比如超執行緒技術,它的實質是在邏輯上(不是物理)映射另一個CPU核心,然後共用CPU的緩衝,以軟體的分配調度方式來類比多核的應用(硬體底層是需要底層的軟體代碼來支援,即晶片內部的資料處理代碼,其上才是作業系統,而作業系統需要再通過裝置驅動程式才能訪問該硬體)。這種支援超執行緒的CPU,在windows工作管理員中,能夠看到2個至多個CPU使用記錄的顯示,但其實質上仍是1/2個硬核(現在的CPU,比如4核,如果支援超執行緒技術,那麼顯示為8個CPU記錄,其它類推)。Intel剛開始推出超執行緒CPU技術,貌似挺好,但是早期的硬體設計、驅動以及作業系統的支援問題,Intel曾一度停止該超執行緒技術的應用。但是到了後來,因為技術成熟了,所以又開始應用該技術到CPU裡。還是實際的硬核才是真道理。

           CPU不知道什麼線程,它只負責處理資料。早期的匯流排型技術,能夠提供的頻寬相對比較小,隨著硬體的發展,已經限制了CPU處理資料的速度,於是,最新的QPI型技術出來了,頻寬增大了,當然,目前只在X58、P55之類的主板上才支援,新技術開始總是貴的。對於CPU來說,它只知道二進位指令(RISC指令集和CISC指令集)和位元據,而資料的長度(位元),即32bit、64bit決定了CPU處理資料的大小。晶片級代碼的演算法,就已經控制和調度哪個閒置CPU核來處理並行的資料。所以,作業系統只需要調用硬體CPU廠商提供的驅動程式,並控制線程隊列的運作即可,實質上做的是中介的應用。

      二、這裡描述下線程的調度順序。 使用者應用程式 -> 作業系統 -> HAL -> 驅動程式 -> 主板北橋晶片集(P55隻有南橋) -> 主板匯流排 -> CPU核心調度演算法 -> CPU指令集 -> CPU緩衝 -> CPU核心 。(這個順序是我對硬體的理解,如果大家有不同的意見,歡迎批評指正)。

           我們用C#編寫的並行程式,受CLR託管,而並行程式中的線程,受作業系統的管理。.NET架構已經提供了對線程的調用的方法集,考慮了線程的建立,更新,通訊,同步,資料鎖,非同步通訊等等問題。所以,除非特別需要,盡量使用架構提供的方法來操作線程,以取得更好的效能和效率以及控制力。

           1、理解線程的生命期。

           主要是對線程的狀態變化進行理解,對於後面理解線程的運行機制和使用代碼控制線程提供基礎。

          

           介紹了C#中線程的操作方法和狀態。應該對該圖有個印象,後面應用這些方法就簡單了。

           2、理解線程所處的環境。

           要使用C#提供的線程操作功能,必須先搞清楚線程所處的運行環境。展示了基本環境。

          

            是一個簡要的環境描述。其中,CLR運行於作業系統上,而託管的應用程式進程,則運行在CLR的控制下。應用程式定義域對應於程式集。每個域裡面,可能沒有線程,也可能會有多個線程。

            3、調用線程。

            C#中調用線程很簡單。Thread t=new Thread(new ThreadStart(Function)); t.Start();即可。這裡線程的回收,也是由GC處理。

            線程的優先順序也同樣比較重要,當然也不能隨意設定,太多的高優先順序的線程將搶佔CPU資源,反而會導致作業系統效能下降。線程的同步和安全執行緒是需要特別注意的地方。如果處理不好,則會導致資源競爭,導致死結等問題。

            線程的同步,.NET架構中提供了幾個操作類進行處理和控制。這個需要對各個操作類的應用深入瞭解,選擇性的進行使用,以提供應用程式效能。

            比如,對共用資源的鎖定及重要程式碼片段的鎖定,一般習慣性的用下列代碼來實現:

代碼1 lock( object )
2 {
3     //do something 
4     //deal with object
5 }

             這個是常用的方式,在IL中產生的程式碼,與使用下面的代碼類似,在IL上沒啥區別。

代碼1 Monitor.Enter( object );
2 //do something 
3 //deal with object 
4 Monitor.Exit();

             這裡有個小問題,Monitor.Enter( object );該方法會在資源object爭用時導致線程等待(從而就會有死結的可能發生),所以適合於該線程處理的內容為需要等待處理結束的應用。而如果是線程對線程的調度,或者線程監控某個資源的應用下,就得使用bool b=Monitor.TryEnter( object );該方法如果擷取不到資源,則b將為false,這樣下面就可以根據b來做分支判斷是否執行處理資料代碼了,否則可以結束該線程,等待下一個新線程對該資源的訪問,從而不用等待資源的釋放。選擇哪個應用取決於實際環境的分析和設計了。

             4、線程池技術。

             線程池技術在多線程程式的效率上節省了建立新線程的時間,轉為對線程資源的調度應用上。當然,線程池也不是萬能的。它主要是應用在短暫的線程運行處理上,而不適用於某個處理大且長的應用上。對於線程池的應用,直接使用.NET架構中的ThreadPool操作類即可,其內建的處理方法與作業系統的配合,是一種高效的應用線程池的方案。

             如果在特殊場合,需要自己建立線程池的話(或者儲存其它與線程類似的對象的對象池),建議盡量使用HashSet<T>泛型類來進行處理,而不要使用數組的方式來進行儲存。該書中就是使用了ArrayList數組來進行處理。線程池一般都固定大小,所以會使用數組來進行處理。但是ArrayList也是長度可變的數組。在對儲存的內容的處理上,數組也是儲存在託管堆上的,但是它的地區是連續的記憶體地區,這個是它的特點,也是優點。而HashSet使用的是Hash的方式進行的儲存,對於儲存內容的處理上,對於增減內容的操作,對比固定數組的處理上要高效,因為數組如果刪除中間的某個內容,需要迴圈以將後續的內容來填充至該刪除的地區,可能降低了效率。在此不多說了,大家可以寫些DEMO來做效能判斷。

            1、我舉個例子。線程池就象是工廠裡面的多條生產線。需要生產產品的時候,我就取一條閒置生產線來進行處理。生產達到任務後就讓這條生產線空出來,等待 下一個生產調用。如果沒有閒置生產線,那麼我會讓該任務等待一下再去處理,或者增加一條生產線來處理任務,實在不行,再根據任務優先順序來暫停某條生產 線,優先處理現在需要處理的任務。。。
           2、對於線程池中的線程,使用完後不是釋放它的資源,而是讓它空閑出來。是我這個“增減”沒有描述清楚,是一個實現方式的問題,才導致了你的誤會,下面說一下。
           3、那麼,線程池怎麼實現呢?
           如果使用一個固定長度的數組來實現的話,那麼,就需要迴圈遍曆數組來尋找空閑可用的線程,在多個請求空閑線程的時候,還需要鎖定該線程資源來保證安全執行緒等等。。。這個是一個實現方式。
          另 一個實現方式,就如書中所描述的。使用一個數組來儲存已在使用線程,使用另一個數組來儲存閒置線程。請求空閑線程,直接從空閑數組中擷取線程,並儲存到 已使用數組中。已使用數組中的線程,完成任務後就儲存到空閑數組中。這個就是我所說的線程“增減”的問題。。。這個是另一個實現方式。
至於這兩種方式,效率和效能的取捨就要看大家怎麼應用了。 

            5、多線程程式的調試

            VS中提供了工具,用來對多線程程式的調試提供了便利。具體請看該書的第6章。

 

            上述是說了幾點,還是沒有將概念講透,請大家仔細閱讀該書。

            下面給出些小參考:

            1、對於CPU硬體來說,主要的在與其啟動並執行頻率高低,決定了它的運行速度。所以,對於一個單核頻率為3.0G的CPU,和一個雙核2.0G的CPU,在使用單線程的應用程式,或者少量的多線程應用程式來說,由於3.0G的運行速度,那麼其將比2.0G的雙核CPU運行得快。而如果多線程的應用程式環境下,2.0G雙核的CPU不定會比單核3.0G運行得快,這個主要是多線程程式會導致CPU頻繁的切換線程,所以,不能片面的說多核的CPU就比單核的CPU速度快。對於目前最新的Core i5的四核CPU,比如2.0G頻率,在硬體上已經做了最佳化,如果啟動並執行主要是單線程的程式,那麼它會把運行頻率提高到3.0或者其它的頻率,同時關閉其它的硬核,以提高運行速度。而在主要運行多線程的程式時,它會根據演算法平均分配CPU資源以加快程式啟動並執行效率。。。

            2、對於多線程程式的編寫,一定要盡量少而精簡的利用線程,以減少CPU對線程的調度切換的時間和效率。

            3、除了使用.NET架構提供的線程操作處理類方法外,還有其它第3方的解決方案,比如Intel就提供了第3方的組件來提供支援,這個可以參考《多核程式設計技術》一書。

            4、ASP.NET程式的運行,本身就是多線程的,所以,如果可以,建議查閱該方面底層的內容,對.NET架構如何應用多線程技術,以及如何提高效率做參考。

            5、可以查閱其它相關C#線程操作方面的書籍。或者找些C#寫的網遊遊戲代碼來做參考。這些都是多線程技術的典型應用方向。

 

            時間過得真快,轉眼又到周末了,祝大家周末愉快吧。該休息的休息,該玩的玩。。。。。。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.