你也會注意到任務管理器中有CPU使用率的信息。這是因為進程也有一個使用計算機處理器的執行順序。這個執行順序就是線程。這個線程由CPU上正在使用的寄存器,線程使用的堆棧以及保存線程當前狀態的存儲器共同定義。存儲器和堆棧的概念對那些經常處理底層內存分配的同僚們來說應該很熟悉;然而,對.NET Framework 中的堆棧來說,你可以把它看成一塊用來快速訪問數據,存儲值類型或者指向對象、方法參數以及每個方法調用的本地數據的內存區域。
單線程進程
正如上面提到的,每個進程至少有一個執行順序(或線程)。創建一個進程包括讓進程開始運行指令。初始線程又稱作原始線程或主線程。線程的實際執行順序由你的程序中的代碼決定。例如,在一個簡單的.NET Windows 窗體應用程序中,主線程在你的工程中的靜態Main()方法處啟動。它開始於對Application.Run()的調用。
現在我們大概知道了什么是一個進程以及它至少有一個線程,讓我們從圖2中的虛擬模型角度看看這種關系。
圖2
讓我們看一下上面的圖,你會發現線程和數據在同一個隔離區間內。這是要說明你在進程中定義的數據可以被線程訪問。線程在處理器上執行同時按需要使用進程中的數據。這看起來很簡單;我們有一個物理隔離的進程,所以其他進程不可能修改這個數據。在這個進程看來,它是系統中正在運行的唯一進程。我們不需要知道其他進程的細節以及它們的關聯線程就可以讓我們自己的進程工作。
說得更准確一些,線程實際上是指向一個進程指令流的一個指針。線程本身並不包含指令,它只是通過由數據和分支決策確定的指令指出了當前以及未來可能的路徑。
時間片
當我們討論多任務時,我們指出操作系統為每個程序分配一定時間,然后中斷當前運行程序並允許另外一個程序執行。這並不完全准確。處理器實際上為進程分配時間。進程可以執行的時間被稱作“時間片”或者“限量”。時間片的間隔對程序員和任何非操作系統內核的程序來說都是變化莫測的。程序員不應該在他們的程序中將時間片的值假定為一個常量。每個操作系統和每個處理器都可能設定一個不同的時間。
不過,我們之前還沒有提到一個涉及並發的潛在問題,而且我們應該考慮如果每個進程都是物理隔離的,那么並發將如何發揮作用。這意味着挑戰才剛剛開始,這也是本書的余下部分要重點關注的。我們提到過一個進程在運行時至少有一個線程。我們的進程在任意一個時間點都可能有多於一個任務。例如,它可能需要通過網絡訪問一個SQL Server 數據庫,同時需要繪出用戶接口。
多線程的進程
你可能已經知道,我們可以將分配給進程的時間片拆開來用。這是通過在進程內額外生成新線程來實現。你可能想生成一個額外線程來做一些后台的工作,比如訪問一個網絡或者查詢一個數據庫。因為這些子線程通常被創建來做一些工作,所以它們通常被稱作工作線程。這些線程共享分配給進程的與系統中其他進程隔離開的內存空間。在一個進程內生成新的線程通常被稱作自由線程。
自由線程的概念相對於單元線程模型有很大的優勢,后者用在Visual Basic 6.0 中。在單元線程中,每個進程都被分配一份它需要的全局數據的拷貝來執行。每個生成的線程都在它自己的進程中生成,以便於線程之間不能共享進程的內存中的數據。我們通過對比來看一下這些模型的差異。圖3描述了單元線程概念,而圖4描述了自由線程概念。我們不需要在這里花費太多時間,但是理解這些差異對我們很重要:
圖3
圖4
正如你看到的,每次當你想做一些后台的工作時,它一般在它自己的進程中進行。因此這又稱作在進程內運行。這個模型與圖4中的線程模型非常不同。
我們得到了使用額外線程的好處以及共享同樣數據的能力。要注意一個重要的問題:在同一個時間點只能有一個線程在處理器上執行。進程中的每個線程會在被分配的執行空間內來進行工作。讓我們再看一遍圖5中的圖表來幫助我們理解它是如何工作的。
圖5
為了便於理解,本書中使用的例子和圖表都假設系統中只有一個處理器。然而,如果計算機中有超過一個處理器的話,你的應用程序使用多線程會高效一些。因為操作系統有兩個位置(處理器)來執行線程的代碼了。還是用我們之前提過的銀行的例子,這類似於我們讓一個新出納開一個新窗口。操作系統負責決定哪個線程運行在哪個處理器上。如果程序員選擇,.NET 平台提供控制一個進程使用哪個CPU的功能。這通過System.Diagnostics.Process.ProcessorAffinity屬性實現。需要注意的是,這個屬性是設置在進程級別的以至於在這個進程中的所有進程都會執行在同樣的處理器上。
調度這些線程要比上一張圖標中描述的復雜很多,但是就我們的目的來說這已經足夠了。由於每個線程都按順序執行,我們可能被銀行工作人員提醒要在銀行櫃台前排隊。我們也要記住這些線程都會在一個很短的時間內被中斷。同時,另外一個線程,可能是同一進程里的,也可能是其他進程里的,會開始執行。在我們繼續探討之前,先看看任務管理器。
運行任務管理器並選中進程選項卡。打開了以后,選擇查看->選擇列面板。你將會看到任務管理器中的列。我們在這里只關心一個列-線程數。選中了以后,你將看到類似以下的內容:
單擊確認以后你將看到很多進程有不止一個線程。這證實了你的程序在一個進程中可能有多個線程的想法。





