CLR 線程基礎

CLR 線程基礎 node

一、什麼是進程:算法

操做系統三個基本的抽象概念:windows

  • 1 文件 : 對 I/O 設備的抽象表示;緩存

  • 2 虛擬內存: 對主存 和 磁盤I/O 設備的抽象;安全

  • 3 進程: 對處理器、主存 和 I/O 設備的抽象表示。網絡

進程是操做系統對一個正在運行的程序的一種抽象。在一個系統上能夠同時運行多個進程,而每一個進程都好像獨佔地使用硬件。併發的執行多個進程,是處理器在進程間切換實現的。操做系統實現這種交錯執行的機制稱爲上下文切換。數據結構

所謂 上下文 ,就是進程運行時所須要的全部狀態信息。好比:PC 和 寄存器文件的當前值,以及主存的內容。當一個進程中止,操做系統將當前進程的上下文保存起來,恢復另外一個進程的上下文,將控制權交給另外一個進程。另外一個進程從它上次中止的地方開始。併發

邏輯控制流:async

程序被編譯成一條條指令和數據,這一系列的程序計數器(PC)的值的序列稱爲:邏輯控制流 每一個進程執行它的邏輯控制流的一部分,而後被搶佔,而後輪到其餘進程。函數

私有地址空間:

每一個進程都有本身的私有地址空間,它關聯內存中一段內容。

用戶模式和內核模式:

內核模式能夠執行任意指令,訪問任意內存地址。進程從用戶模式切換到內核模式惟一的方法是經過諸如中斷、故障、或者陷入系統調用這樣的異常。

上下文切換的時機:

1 內核表明用戶執行系統調用時;

2 sleep 系統調用;

3 中斷也可引發上下文切換。中斷:系統都有某種產生週期性定時器中斷機制,1- 10 毫秒,每次發生中斷時內核會判斷當前進程運行的時間,是否須要切換到另外一個進程。

在從A 進程切換到B 進程以前,內核表明進程A 在用戶模式下執行指令。在切換的第一部分,內核表明進程A 在內核模式下執行指令。而後在某一時刻,它開始表明進程B 執行指令(任然是在內核模式下)。在切換以後,內核表明進程B 在用戶模式下執行指令。

 

二、線程的概念

進程是對對處理器、主存 和 I/O 設備的抽象表示,那線程就是對CPU計算資源的抽象表示。Windows 爲每一個進程都提供了該進程專用的線程(功能至關於一個CPU)。進程中的主線程,是建立進程時爲其分配的。進程要作任何事情,都必須讓一個線程在它的上下文中運行。該線程負責執行進程地址空間包含的代碼。每一個線程都有本身的一組 CPU 寄存器和 它本身的堆棧。

線程開銷

每一個線程都有如下要素:

  • 線程內核對象(thread kernel object)

    線程內核對象你能夠把它想象成是一種數據結構,它保存在OS 爲線程分配的內存中。這個數據結構包含:

    • 1 描述線程的屬性。

    • 2 線程上下文(包含CPU 寄存器集合的內存塊)。

      x86 佔700字節,x64佔1240字節,ARM 佔350 字節。

  • 線程環境塊(thread environment block,TEB)

    TEB 是在用戶模式(應用程序代碼能快速訪問的地址空間)中分配和初始化的內存塊。TEB 耗用1 個內存頁(x8六、x6四、ARM 都是4KB)。

    TEB包含線程的異常處理鏈首(head)。線程進入的每一個try 塊都在鏈首插入一個節點(node);線程退出try塊時從鏈中刪除該節點。

    此外,TEB還包含線程的「線程本地存儲」數據,以及由GDI(Graphics Device Interface,圖形設備接口)和 OpenGL 圖形使用的一些數據結構。

  • 用戶模式棧(user-mode stack)

    用戶模式棧存儲傳給方法的局部變量和實參。它還包含一個地址:指出當前方法返回時,線程應該從什麼地方接着執行。

    Windows 默認爲每一個線程的用戶模式棧分配1MB內存。更具體地說,windows 只是保留1 MB地址空間,在線程實際須要時纔會提交物理內存。

  • 內核模式(kernel-mode stack)

    應用程序代碼向操做系統中的內核模式函數傳遞實參時,還會使用內核模式棧。出於對安全的考慮,針對從用戶模式的代碼傳給內核的任何實參,Windows 都會把它們從線程的用戶模式棧複製到線程的內核模式棧。32 位windows 內核模式棧 12KB,64位windows是24 KB。

  • DLL 線程鏈接(attach)和線程分離(detach)通知

    windows的一個策略是,任什麼時候候在進程中建立線程,都會調用進程中加載的全部非託管DLL的 DllMain方法,並向該方法傳遞DLL_THREAD_ATTACH 標誌。

    相似地,任什麼時候候線程終止,都會調用進程中的全部非託管DLL 的DllMain方法,並向方法傳遞DLL_THREAD_DETACH 標誌。

    有的DLL 須要獲取這些通知,才能爲進程中建立/銷燬的每一個線程執行特殊的初始化或(資源)清理操做。

 

咱們目前使用的電腦,隨便一個進程也得由幾百個DLL,要是每次在應用程序中建立一個線程,都必須先調用幾百個DLL 函數,而後線程才能開始作它想作的事。在線程終止時還須要將這幾百個DLL 函數再調用一遍。這嚴重影響在進程中建立和銷燬線程的性能。

 

CPU如何上下文切換線程

1 CPU要切換另外一個線程,首先要保存當前線程的狀態,將要執行的線程數據緩存在高速cache中,高速運行30毫秒後再切換線程. windows大約每30毫秒執行一次上下文切換.

2 正在等待IO操做完成的線程,不會被CPU調度.因此不會浪費CPU時間,

3 一個時間片結束時,若是Windows再次調度同一個線程,那麼Windows不會執行上下文切換,接着當前線程繼續執行.

儘可能避免使用線程,它們消耗大量的內存,須要不少時間來建立,銷燬,管理.windows在線程間上下文切換,以及在發生垃圾回收的時候,也會浪費很多時間.

若是隻關心性能,任何機器最優的線程數就是它擁有的CPU核數.可是Microsoft設計windows時,追求的是可靠性,和響應能力.

 

對線程的使用盡可能從線程池中獲取。在極少數狀況可能須要顯示建立線程來專門執行一個計算限制的操做。知足如下任何條件,就能夠顯示建立本身的線程:

  • 線程須要以非普通線程優先級運行。全部線程池線程都以普通優先級運行;能夠更改線程池的線程優先級,可是不建議那麼作。

  • 須要線程表現爲一個前臺線程,防止應用程序在線程結束任務前終止。線程池是始終是後臺線程,若是CLR想終止進程,後臺線程會跟着終止。

  • 計算限制的任務須要長時間運行。線程池爲了判斷是否須要建立一個額外的線程,所採用的邏輯是比較複雜的。直接爲長時間運行的任務建立專用線程,就能夠避免這個問題。

  • 要啓動線程,並可能調用thread 的abort 方法來提早終止它。

要建立專用線程,要構造System.Threading.Thread 類的實例,想構造器傳遞一個方法名。

using System;
using System.Threading;
public static class Program {
   public static void Main() {
       Console.WriteLine("Main thread: starting a dedicated thread " +
       "to do an asynchronous operation");
       Thread dedicatedThread = new Thread(ComputeBoundOp);
       dedicatedThread.Start(5);
       Console.WriteLine("Main thread: Doing other work here...");
       Thread.Sleep(10000); // Simulating other work (10 seconds)
       dedicatedThread.Join(); // Wait for thread to terminate
       Console.WriteLine("Hit <Enter> to end this program...");
       Console.ReadLine();
  }
   // This method's signature must match the ParameterizedThreadStart delegate
   private static void ComputeBoundOp(Object state) {
       // This method is executed by a dedicated thread
       Console.WriteLine("In ComputeBoundOp: state={0}", state);
       Thread.Sleep(1000); // Simulates other work (1 second)
       // When this method returns, the dedicated thread dies
  }
}

編譯並運行上述代碼可能獲得如下輸出:

Main thread: starting a dedicated thread to do an asynchronous operation
Main thread: Doing other work here...
In ComputeBoundOp: state=5

但也可能獲得如下輸出:

Main thread: starting a dedicated thread to do an asynchronous operation
In ComputeBoundOp: state=5
Main thread: Doing other work here...

 

線程調度和優先級

搶佔式操做系統必須使用算法判斷在何時調度哪些線程多長時間。前面說過每一個線程的內核對象都包含一個上下文結構。上下文結構反映了線程上一次執行完畢後 CPU寄存器的狀態。在一個時間片(time-slice)以後,widows檢查現存的全部線程內核對象。在這些對象中,只有哪些沒有正在等待什麼的線程才適合調度。windows 選擇一個可調度的線程內核對象,並上下文切換到它。windows還記錄了每一個線程被上下文切換的次數。

windows 系統作不到很精確的控制線程在你想要的某一時刻執行想要執行的程序。例如:怎樣保證一個線程在網絡有數據傳來的1毫秒內開始運行?這個windows辦不到。實時操做系統能夠作出這種保證。CLR 使託管代碼的行爲變得更不「實時」。

windows 支持6 個進程優先級類:

  • Idle、Below Normal、Normal、Above Normal、High 和 Realtime。默認的Normal 是最經常使用的優先級。

Realtime 優先級要儘量的避免使用,它的優先級至關高,能夠干擾操做系統任務。

windows 支持7 個相對線程優先級:

  • Idle、Lowest、Below Normal、Normal、Above Normal、Highest 和 Time-Critical。

這些優先級是相對於進程優先級類而言的。一樣 Normal 是默認優先級。

系統將進程優先級類和其中的一個線程相對優先級映射成一個優先級(0~31)。下表總結了它們的關係。

 

 注意:

  • 表中沒有值爲0 的優先級,由於0優先級保留給零頁線程。系統不容許其餘線程的優先級爲0。

  • 並且,如下優先級也不能夠得到:17,18,19,20,21,27,28,29 或 30。之內核模式運行的設備驅動程序才能得到這些優先級。

  • Realtime 優先級類中的線程優先級不能低於16。

  • 非Realtime 的優先級類中的線程優先級不能高於15。

  • 託管應用程序不該該表現爲擁有它們本身的進程;相反,它們應該表現爲在一個AppDomain 中運行。因此,託管應用程序不該該更改它們的進程的優先級,由於這會影響進程中運行的全部代碼。

 

你的應用程序能夠更改其線程的相對優先級,這須要設置 Thread 的 Priority 屬性,向其傳遞 ThreadPriority 枚舉類型定義的5 個值之一:Lowest、BelowNormal、Normal、AboveNormal 或者 Highest。

 

線程是很是寶貴的資源,必須省着用。使用線程最好的方式就是從線程池中獲取。由線程池來管理線程的建立和銷燬,這是最可靠的。

相關文章
相關標籤/搜索