操作系統通過線程對程序的執行進行管理,當操作系統運行一個程序的時候,首先,操作系統將為這個准備運行的程序分配一個進程,以管理這個程序所需要的各種資源。在這些資源之中,會包含一個稱為主線程的線程數據結構,用來管理這個程序的執行狀態。
在Windows操作系統下,線程的的數據結構包含以下內容:
1、線程的核心對象:主要包含線程當前的寄存器狀態,當操作系統調度這個線程開始運行的時候,寄存器的狀態將被加載到CPU中,重新構建線程的執行環境,當線程被調度出來的時候,最后的寄存器狀態被重新保存到這里,已備下一次執行的時候使用。
2、線程環境塊(Thread Environment Block,TED):是一塊用戶模式下的內存,包含線程的異常處理鏈的頭部。另外,線程的局部存儲數據(Thread Local Storage Data)也存在這里。
3、用戶模式的堆棧:用戶程序的局部變量和參數傳遞所使用的堆棧,默認情況下,Windows將會被分配1M的空間用於用戶模式堆棧。
4、內核模式堆棧:用於訪問操作系統時使用的堆棧。
在搶先式多任務的環境下,在一個特定的時間,CPU將一個線程調度進CPU中執行,這個線程最多將會運行一個時間片的時間長度,當時間片到期之后,操作系統將這個線程調度出CPU,將另外一個線程調度進CPU,我們通常稱這種操作為上下文切換。
在每一次的上下文切換時,Windows將執行下面的步驟:
- 將當前的CPU寄存器的值保存到當前運行的線程數據結構中,即其中的線程核心對象中。
- 選中下一個准備運行的線程,如果這個線程處於不同的進程中,那么,還必須首先切換虛擬地址空間。
- 加載准備運行線程的CPU寄存器狀態到CPU中。
公共語言運行時CLR(Common Language Runtime)是.Net程序運行的環境,它負責資源管理,並保證應用和底層操作系統之間必要的分離。
在.Net環境下,CLR中的線程需要通過操作系統的線程完成實際的工作,目前情況下,.Net直接將CLR中的線程映射到操作系統的線程進行處理和調度,所以,我們每創建一個線程將會消耗1M以上的內存空間。但未來CLR中的線程並不一定與操作系統中的線程完全對應。通過創建CLR環境下的邏輯線程,我們可能創建更加節省資源的線程,使得大量的CLR線程可以工作在少量的操作系統線程之上。
1. System.Threading.Thread類
System.Threading.Thread是用於控制線程的基礎類,通過Thread可以控制當前應用程序域中線程的創建、掛起、停止、銷毀。
它包括以下常用公共屬性:
屬性名稱 | 說明 |
---|---|
CurrentContext | 獲取線程正在其中執行的當前上下文。 |
CurrentThread | 獲取當前正在運行的線程。 |
ExecutionContext | 獲取一個 ExecutionContext 對象,該對象包含有關當前線程的各種上下文的信息。 |
IsAlive | 獲取一個值,該值指示當前線程的執行狀態。 |
IsBackground | 獲取或設置一個值,該值指示某個線程是否為后台線程。 |
IsThreadPoolThread | 獲取一個值,該值指示線程是否屬於托管線程池。 |
ManagedThreadId | 獲取當前托管線程的唯一標識符。 |
Name | 獲取或設置線程的名稱。 |
Priority | 獲取或設置一個值,該值指示線程的調度優先級。 |
ThreadState | 獲取一個值,該值包含當前線程的狀態。 |
常用屬性示例:

using System; using System.Threading; namespace ConsoleApp { class Program { static void Main(string[] args) { //新建3個線程並設定各自的優先級 Thread t1 = new Thread(Run); t1.Priority = ThreadPriority.Normal; t1.Start(); Console.ReadKey(); } public static void Run() { Thread t1 = Thread.CurrentThread; //靜態屬性,獲取當前執行這行代碼的線程 Console.WriteLine("我的優先級是:" + t1.Priority); Console.WriteLine("我是否還在執行:" + t1.IsAlive); Console.WriteLine("是否是后台線程:" + t1.IsBackground); Console.WriteLine("是否是線程池線程:" + t1.IsThreadPoolThread); Console.WriteLine("線程唯一標識符:" + t1.ManagedThreadId); Console.WriteLine("我的名稱是:" + t1.Name); Console.WriteLine("我的狀態是:" + t1.ThreadState); } } }
2. 線程的標識符
ManagedThreadId是確認線程的唯一標識符,程序在大部分情況下都是通過Thread.ManagedThreadId來辨別線程的。而Name是一個可變值,在默認時候,Name為一個空值 Null,開發人員可以通過程序設置線程的名稱,但這只是一個輔助功能。
3. 線程的優先級別
.NET為線程設置了Priority屬性來定義線程執行的優先級別,里面包含5個選項,其中Normal是默認值。除非系統有特殊要求,否則不應該隨便設置線程的優先級別。
成員名稱 | 說明 |
---|---|
Lowest | 可以將 Thread 安排在具有任何其他優先級的線程之后。 |
BelowNormal | 可以將 Thread 安排在具有 Normal 優先級的線程之后,在具有 Lowest 優先級的線程之前。 |
Normal | 默認選擇。可以將 Thread 安排在具有 AboveNormal 優先級的線程之后,在具有BelowNormal 優先級的線程之前。 |
AboveNormal | 可以將 Thread 安排在具有 Highest 優先級的線程之后,在具有 Normal 優先級的線程之前。 |
Highest | 可以將 Thread 安排在具有任何其他優先級的線程之前。 |
優先級的示例:

using System; using System.Threading; namespace ConsoleApp { class Program { static void Main(string[] args) { //新建3個線程並設定各自的優先級 Thread t1 = new Thread(Run); t1.Priority = ThreadPriority.Lowest; Thread t2 = new Thread(Run); t2.Priority = ThreadPriority.Normal; Thread t3 = new Thread(Run); t3.Priority = ThreadPriority.Highest; //由低到高優先級的順序依次調用 t1.Start(); t2.Start(); t3.Start(); Console.ReadKey(); } public static void Run() { Console.WriteLine("我的優先級是:" + Thread.CurrentThread.Priority); } } }
4. 線程的狀態
通過ThreadState可以檢測線程是處於Unstarted、Sleeping、Running 等等狀態,它比 IsAlive 屬性能提供更多的特定信息。
前面說過,一個應用程序域中可能包括多個上下文,而通過CurrentContext可以獲取線程當前的上下文。
CurrentThread是最常用的一個屬性,它是用於獲取當前運行的線程。
5. System.Threading.Thread的方法
Thread 中包括了多個方法來控制線程的創建、掛起、停止、銷毀,以后來的例子中會經常使用。
方法名稱 | 說明 |
---|---|
Abort() | 終止本線程。 |
GetDomain() | 返回當前線程正在其中運行的當前域。 |
GetDomainId() | 返回當前線程正在其中運行的當前域Id。 |
Interrupt() | 中斷處於 WaitSleepJoin 線程狀態的線程。 |
Join() | 已重載。 阻塞調用線程,直到某個線程終止時為止。 |
Resume() | 繼續運行已掛起的線程。 |
Start() | 執行本線程。 |
Suspend() | 掛起當前線程,如果當前線程已屬於掛起狀態則此不起作用 |
Sleep() | 把正在運行的線程掛起一段時間。 |
6. 開發實例
前台線程與后台線程的區別
我們看到上面有個屬性叫后台線程,非后台線程就叫前台線程吧,Thread.Start()啟動的線程默認為前台線程,啟動程序時創建的主線程一定是前台線程。應用程序與必須等到所有的前台線程執行完畢才會卸載。而當IsBackground設置為true時,就是后台線程了,當主線程執行完畢后就直接卸載,不再理會后台線程是否執行完畢。
前台與后台線程的設置必須在線程啟動之前進行設置,線程啟動之后就不能設置了。
Thread創建的線程是前台線程,線程池中的是后台線程。

using System; using System.Threading; namespace ConsoleApp1 { public delegate string MethodCaller(string name);//定義個代理 class Program { static void CountNumbers() { int _iterations = 3; for (int i = 1; i <= _iterations; i++) { Thread.Sleep(TimeSpan.FromSeconds(0.5)); Console.WriteLine("{0} prints {1}", Thread.CurrentThread.Name, i); } } static void Count(object iterations) { CountNumbers((int)iterations); } static void CountNumbers(int iterations) { for (int i = 1; i <= iterations; i++) { Thread.Sleep(TimeSpan.FromSeconds(0.5)); Console.WriteLine("{0} prints {1}", Thread.CurrentThread.Name, i); } } static void PrintNumber(int number) { Console.WriteLine(number); } static string GetName(string name) // 函數 { int _iterations = 5; for (int i = 1; i <= _iterations; i++) { Thread.Sleep(TimeSpan.FromSeconds(0.5)); Console.WriteLine("{0} prints {1}", Thread.CurrentThread.Name, i); } return name; } static void Main(string[] args) { //無參數的調用 var threadOne = new Thread(CountNumbers); threadOne.Name = "ThreadOne"; threadOne.Start(); threadOne.Join(); Console.WriteLine("--------------------------"); //帶參數的調用方式一 var threadTwo = new Thread(Count); threadTwo.Name = "ThreadTwo"; threadTwo.Start(3); threadTwo.Join(); Console.WriteLine("--------------------------"); //帶參數的調用方式二 var threadThree = new Thread(() => CountNumbers(4)); threadThree.Name = "ThreadThree"; threadThree.Start(); threadThree.Join(); Console.WriteLine("--------------------------"); //注意在多個lambda表達式中使用香港的變量,它們會共享該變量 這里兩個線程的參數都是20 int i = 10; var threadFour = new Thread(() => PrintNumber(i)); i = 20; var threadFive = new Thread(() => PrintNumber(i)); threadFour.Start(); threadFive.Start(); Console.WriteLine("--------------------------"); MethodCaller mc = new MethodCaller(GetName); string name = "my name";//輸入參數 IAsyncResult result = mc.BeginInvoke(name, null, null); Console.WriteLine("繼續主線程的業務"); string myname = mc.EndInvoke(result);//用於接收返回值 Console.WriteLine(string.Format("result={0}", myname)); } } }
2、等待線程結束,會阻塞當前主線程(.Join)

using System; using System.Threading; namespace ConsoleApp1 { class Program { static void Main(string[] args) { Console.WriteLine("Starting program..."); Thread t = new Thread(PrintNumbersWithDelay); t.Start(); t.Join(); Console.WriteLine("Thread completed"); } static void PrintNumbersWithDelay() { Console.WriteLine("Starting..."); for (int i = 1; i <= 5; i++) { Thread.Sleep(TimeSpan.FromSeconds(1)); Console.WriteLine(i); } } } }
3、線程中異常的處理只能在線程調用中的函數里面去處理,在外面是接收不到線程中的報錯的

using System; using System.Threading; namespace ConsoleApp1 { class Program { static void Main(string[] args) { var t = new Thread(FaultyThread); t.Start(); t.Join(); //try //{ // t = new Thread(BadFaultyThread); // t.Start(); //} //catch (Exception ex) //{ // //這里捕獲不了線程中的異常 // Console.WriteLine("We won't get here!"); //} } static void BadFaultyThread() { Console.WriteLine("Starting a Badfaulty thread..."); Thread.Sleep(TimeSpan.FromSeconds(2)); throw new Exception("Boom!"); } static void FaultyThread() { try { Console.WriteLine("Starting a faulty thread..."); Thread.Sleep(TimeSpan.FromSeconds(1)); throw new Exception("Boom!"); } catch (Exception ex) { Console.WriteLine("Exception handled: {0}", ex.Message); } } } }
4、設置線程優先級(.Priority)

using System; using System.Diagnostics; using System.Threading; namespace ConsoleApp1 { class Program { static void Main(string[] args) { Console.WriteLine("Current thread priority: {0}", Thread.CurrentThread.Priority); Console.WriteLine("Running on all cores available"); RunThreads(); Thread.Sleep(TimeSpan.FromSeconds(2)); Console.WriteLine("Running on a single core"); Process.GetCurrentProcess().ProcessorAffinity = new IntPtr(1); RunThreads(); Console.ReadLine(); } static void RunThreads() { var sample = new ThreadSample(); var threadOne = new Thread(sample.CountNumbers); threadOne.Name = "ThreadOne"; var threadTwo = new Thread(sample.CountNumbers); threadTwo.Name = "ThreadTwo"; threadOne.Priority = ThreadPriority.Lowest; threadTwo.Priority = ThreadPriority.Highest; threadOne.Start(); threadTwo.Start(); Thread.Sleep(TimeSpan.FromSeconds(2)); sample.Stop(); } class ThreadSample { private bool _isStopped = false; public void Stop() { _isStopped = true; } public void CountNumbers() { long counter = 0; while (!_isStopped) { counter++; } Console.WriteLine("{0} with {1,11} priority " + "has a count = {2,13}", Thread.CurrentThread.Name, Thread.CurrentThread.Priority, counter.ToString("N0")); } } } }