引言:
最近一段時間都在研究關於.Net線程的內容,覺得線程是每個程序員都應該掌握的,所以寫下這個線程的系列希望能給大家學習過程中一些幫助,同時也是自己對線程的鞏固,當中如果有什么錯漏還請大家指出,這樣我們可以互相得到進步。
目錄:
一、線程的介紹
二、線程調度和優先級
三、前台線程和后台線程
四、簡單線程的使用
一、線程的介紹
在介紹線程之前, 很有必要知道什么是進程,以及與線程的關系。
進程(Process)是應用程序的實例要使用的資源的一個集合(從可以簡化理解:進程就是一種資源,是應用程序所用的資源)。每個應用程序都在各自的進程中運行來確保應用程序不受其他應用程序的影響,如果一個應用程序失敗了, 只會影響自己的進程,其他進程中的應用程序可以繼續運行。進程是操作系統為我們提供的一種保護應用程序的一種機制。
線程是進程中基本執行單元, 一個進程中可以包含多個線程,在進程入口執行的第一個線程是一個進程的主線程,在.Net應用程序中,都是以Main()方法作為程序的入口的, 所以在程序運行過程中調用這個方法時,系統就會自動創建一個主線程。(他們之間的關系簡單說:線程是進程的執行單元,進程是線程的一個容器了)。
二、線程調度和優先級
Windows之所以被稱為搶占式多線程操作系統,是因為線程可以在任意時間被搶占,並調度另一個線程。每個線程都分配了從0~31的一個優先級。系統首先把高優先級的線程分配給CPU執行。Windows 支持7個相對線程優先級:Idle,Lowest,Below Normal,Normal,Above Normal,Highest和Time-Critical,Normal是默認的線程優先級,然而在程序中可以通過設置Thread的Priority屬性來改變線程的優先級,它的類型為ThreadPriority枚舉類型,包含枚舉有:Lowest,BelowNormal,Normal,AboveNormal和Highest,CLR為自己保留了 Idle和Time-Critical優先級。具體每個枚舉值含義如下表:
成員名稱 | 說明 |
---|---|
Lowest | 可以將 Thread何其他優先級的線程之后。 |
BelowNormal | 可以將 Thread Normal 優先級的線程之后,在具有 Lowest 優先級的線程之前。 |
Normal | 可以將 Thread AboveNormal 優先級的線程之后,在具有 BelowNormal 優先級的線程之前。 默認情況下,線程具有 Normal 優先級。 |
AboveNormal | 可以將 Thread Highest 優先級的線程之后,在具有 Normal 優先級的線程之前。 |
Highest | 可以將 Thread 其他優先級的線程之前。 |
三、前台線程和后台線程
在.net中線程分為前台線程和后台線程,在一個進程中,當所有前台線程停止運行時,CLR會強制結束仍在運行的任何后台線程,這些后台線程直接被終止,不會拋出異常。
所以我們應該在前台線程中執行我們確實要完成的事情,另外, 應該把非關鍵的任務使用后台線程,我們用Thread創建的是線程為前台線程。讓我們通過下面的一段代碼來看看前台線程和后台線成的區別:
using System; using System.Threading; class Program { static void Main(string[] args) { // 創建一個新線程(默認為前台線程) Thread backthread = new Thread(Worker); // 使線程成為一個后台線程 backthread.IsBackground = true; // 通過Start方法啟動線程 backthread.Start(); // 如果backthread是前台線程,則應用程序大約5秒后才終止 // 如果backthread是后台線程,則應用程序立即終止 Console.WriteLine("Return from Main Thread"); } private static void Worker() { // 模擬做10秒 Thread.Sleep(5000); // 下面語句,只有由一個前台線程執行時,才會顯示出來 Console.WriteLine("Return from Worker Thread"); } }
運行上面代碼可以發現:控制台中顯示字符串: Return form Main Thread 后就退出了, 字符串 Return from Worker Thread字符串根本就沒有顯示,這是因為此時的backthread線程為后台線程,當主線程(執行Main方法的線程,主線程當然也是前台線程了)結束運行后,CLR會強制終止后台線程的運行,整個進程就被銷毀了,並不會等待后台線程運行完后才銷毀。如果把 backthread.IsBackground = true; 注釋掉后, 就可以看到控制台過5秒后就輸出 Return from Worker Thread。再在Worker方法最后加一句 代碼:Console.Read(); 就可以看到這樣的結果了:
注意:有些人可能會問我不想把 backthread.IsBackground = true;注釋掉, 又想把Worker()方法中的字符串輸出在控制台上怎么做呢? 其實是有解決的辦法的, 我們可以調用thread.Join()方法來實現,Join()方法能保證主線程(前台線程)在異步線程thread(后台線程)運行結束后才會運行。
實現代碼如下:
using System; using System.Threading; class Program { static void Main(string[] args) { // 創建一個新線程(默認為前台線程) Thread backthread = new Thread(Worker); // 使線程成為一個后台線程 backthread.IsBackground = true; // 通過Start方法啟動線程 backthread.Start(); backthread.Join(); // 模擬主線程的輸出 Thread.Sleep(2000); Console.WriteLine("Return from Main Thread"); Console.Read(); } private static void Worker() { // 模擬做3秒 Thread.Sleep(3000); // 下面語句,只有由一個前台線程執行時,才會顯示出來 Console.WriteLine("Return from Worker Thread"); } }
運行結果(調用Join方法后后台線程會阻塞主線程所以主線程會后輸出):
四、簡單線程的使用
其實在上面介紹前台線程和后台線程的時候已經通過ThreadStart委托創建了一個線程了,此時已經實現了一個多線程的一個過程,為此系列中將多線程也是做一個鋪墊吧。下面通過ParameterizedThreadStart委托的方式來實現多線程。
以ParameterizedThreadStart委托的方式來實現多線程:
using System; using System.Threading; class Program { static void Main(string[] args) { // 創建一個新線程(默認為前台線程) Thread backthread = new Thread(new ParameterizedThreadStart(Worker)); // 通過Start方法啟動線程 backthread.Start("123"); // 如果backthread是前台線程,則應用程序大約5秒后才終止 // 如果backthread是后台線程,則應用程序立即終止 Console.WriteLine("Return from Main Thread"); } private static void Worker(object data) { // 模擬做5秒 Thread.Sleep(5000); // 下面語句,只有由一個前台線程執行時,才會顯示出來 Console.WriteLine(data + " Return from Worker Thread"); Console.Read(); } }
注意:此時Worker方法傳入了一個參數,並且Start方法也傳遞了一個字符傳參數。 對比與之前創建Thread的不同,
運行結果為:
寫到這里, 本系列的第一篇差不多講完了,在后續的文章將會介紹Thread方法的使用以及通過一些例子來展示他們的不同之處(像Abort()方法Interrupt方法等)對於線程的一些高級使用(如線程池,並行編程和PLINQ、線程同步和計時器)都會在后續中講到。希望本系列可以給初學線程的人有所幫助。