c# 多線程
線程 被定義為程序的執行路徑。每個線程都定義了一個獨特的控制流。如果您的應用程序涉及到復雜的和耗時的操作,那么設置不同的線程執行路徑往往是有益的,每個線程執行特定的工作。
線程是輕量級進程。一個使用線程的常見范例是現代操作系統中并行編程的實現。使用線程節省了 cpu 周期的浪費,同時提高了應用程序的效率。
到目前為止我們編寫的程序是一個單線程作為應用程序的運行范例的單一的過程運行的。但是,這樣子應用程序同時只能執行一個任務。為了同時執行多個任務,它可以被劃分為更小的線程。
1. 線程生命周期
線程生命周期開始于 system.threading.thread 類的對象被創建時,結束于線程被終止或完成執行時。
下面列出了線程生命周期中的各種狀態:
- 未啟動狀態:當線程范例被創建但 start 方法未被調用時的狀況。
- 就緒狀態:當線程準備好運行并等待 cpu 周期時的狀況。
- 不可運行狀態:下面的幾種情況下線程是不可運行的:
- 已經調用 sleep 方法
- 已經調用 wait 方法
- 通過 i/o 操作阻塞
- 死亡狀態:當線程已完成執行或已中止時的狀況。
2. 主線程
在 c# 中,system.threading.thread 類用于線程的工作。它允許創建并訪問多線程應用程序中的單個線程。進程中第一個被執行的線程稱為主線程。
當 c# 程序開始執行時,主線程自動創建。使用 thread 類創建的線程被主線程的子線程調用。您可以使用 thread 類的 currentthread 屬性訪問線程。
下面的程序演示了主線程的執行:
using system; using system.threading; namespace multithreadingapplication { ? ? class mainthreadprogram ? ? { ? ? ? ? static void main(string[] args) ? ? ? ? { ? ? ? ? ? ? thread th = thread.currentthread; ? ? ? ? ? ? th.name = "mainthread"; ? ? ? ? ? ? console.writeline("this is {0}", th.name); ? ? ? ? ? ? console.readkey(); ? ? ? ? } ? ? } }
當上面的代碼被編譯和執行時,它會產生下列結果:
this is mainthread
3. thread 類常用的屬性和方法
下表列出了 thread 類的一些常用的 屬性:
屬性 | 描述 |
---|---|
currentcontext | 獲取線程正在其中執行的當前上下文。 |
currentculture | 獲取或設置當前線程的區域性。 |
currentprincipal | 獲取或設置線程的當前負責人(對基于角色的安全性而言)。 |
currentthread | 獲取當前正在運行的線程。 |
currentuiculture | 獲取或設置資源管理器使用的當前區域性以便在運行時查找區域性特定的資源。 |
executioncontext | 獲取一個 executioncontext 對象,該對象包含有關當前線程的各種上下文的信息。 |
isalive | 獲取一個值,該值指示當前線程的執行狀態。 |
isbackground | 獲取或設置一個值,該值指示某個線程是否為后臺線程。 |
isthreadpoolthread | 獲取一個值,該值指示線程是否屬于托管線程池。 |
managedthreadid | 獲取當前托管線程的唯一標識符。 |
name | 獲取或設置線程的名稱。 |
priority | 獲取或設置一個值,該值指示線程的調度優先級。 |
threadstate | 獲取一個值,該值包含當前線程的狀態。 |
下表列出了 thread 類的一些常用的 方法:
序號 | 方法名 & 描述 |
---|---|
1 | public void abort()在調用此方法的線程上引發 threadabortexception,以開始終止此線程的過程。調用此方法通常會終止線程。 |
2 | public static localdatastoreslot allocatedataslot()在所有的線程上分配未命名的數據槽。為了獲得更好的性能,請改用以 threadstaticattribute 屬性標記的字段。 |
3 | public static localdatastoreslot allocatenameddataslot( string name) 在所有線程上分配已命名的數據槽。為了獲得更好的性能,請改用以 threadstaticattribute 屬性標記的字段。 |
4 | public static void begincriticalregion()通知主機執行將要進入一個代碼區域,在該代碼區域內線程中止或未經處理的異常的影響可能會危害應用程序域中的其他任務。 |
5 | public static void beginthreadaffinity()通知主機托管代碼將要執行依賴于當前物理操作系統線程的標識的指令。 |
6 | public static void endcriticalregion()通知主機執行將要進入一個代碼區域,在該代碼區域內線程中止或未經處理的異常僅影響當前任務。 |
7 | public static void endthreadaffinity()通知主機托管代碼已執行完依賴于當前物理操作系統線程的標識的指令。 |
8 | public static void freenameddataslot(string name)為進程中的所有線程消除名稱與槽之間的關聯。為了獲得更好的性能,請改用以 threadstaticattribute 屬性標記的字段。 |
9 | public static object getdata( localdatastoreslot slot ) 在當前線程的當前域中從當前線程上指定的槽中檢索值。為了獲得更好的性能,請改用以 threadstaticattribute 屬性標記的字段。 |
10 | public static appdomain getdomain()返回當前線程正在其中運行的當前域。 |
11 | public static appdomain getdomainid()返回唯一的應用程序域標識符。 |
12 | public static localdatastoreslot getnameddataslot( string name ) 查找已命名的數據槽。為了獲得更好的性能,請改用以 threadstaticattribute 屬性標記的字段。 |
13 | public void interrupt()中斷處于 waitsleepjoin 線程狀態的線程。 |
14 | public void join()在繼續執行標準的 com 和 sendmessage 消息泵處理期間,阻塞調用線程,直到某個線程終止為止。此方法有不同的重載形式。 |
15 | public static void memorybarrier()按如下方式同步內存存取:執行當前線程的處理器在對指令重新排序時,不能采用先執行 memorybarrier 調用之后的內存存取,再執行 memorybarrier 調用之前的內存存取的方式。 |
16 | public static void resetabort()取消為當前線程請求的 abort。 |
17 | public static void setdata( localdatastoreslot slot, object data ) 在當前正在運行的線程上為此線程的當前域在指定槽中設置數據。為了獲得更好的性能,請改用以 threadstaticattribute 屬性標記的字段。 |
18 | public void start()開始一個線程。 |
19 | public static void sleep( int millisecondstimeout ) 讓線程暫停一段時間。 |
20 | public static void spinwait( int iterations ) 導致線程等待由 iterations 參數定義的時間量。 |
21 | public static byte volatileread( ref byte address ) public static double volatileread( ref double address ) public static int volatileread( ref int address ) public static object volatileread( ref object address ) 讀取字段值。無論處理器的數目或處理器緩存的狀態如何,該值都是由計算機的任何處理器寫入的最新值。此方法有不同的重載形式。這里只給出了一些形式。 |
22 | public static void volatilewrite( ref byte address, byte value ) public static void volatilewrite( ref double address, double value ) public static void volatilewrite( ref int address, int value ) public static void volatilewrite( ref object address, object value ) 立即向字段寫入一個值,以使該值對計算機中的所有處理器都可見。此方法有不同的重載形式。這里只給出了一些形式。 |
23 | public static bool yield()導致調用線程執行準備好在當前處理器上運行的另一個線程。由操作系統選擇要執行的線程。 |
4. 創建線程
線程是通過擴展 thread 類創建的。擴展的 thread 類調用 start() 方法來開始子線程的執行。
下面的程序演示了這個概念:
using system; using system.threading; namespace multithreadingapplication { ? ? class threadcreationprogram ? ? { ? ? ? ? public static void calltochildthread() ? ? ? ? { ? ? ? ? ? ? console.writeline("child thread starts"); ? ? ? ? } ? ? ? ? ? ? ? ? static void main(string[] args) ? ? ? ? { ? ? ? ? ? ? threadstart childref = new threadstart(calltochildthread); ? ? ? ? ? ? console.writeline("in main: creating the child thread"); ? ? ? ? ? ? thread childthread = new thread(childref); ? ? ? ? ? ? childthread.start(); ? ? ? ? ? ? console.readkey(); ? ? ? ? } ? ? } }
當上面的代碼被編譯和執行時,它會產生下列結果:
in main: creating the child thread child thread starts
5. 管理線程
thread 類提供了各種管理線程的方法。
下面的范例演示了 sleep() 方法的使用,用于在一個特定的時間暫停線程。
using system; using system.threading; namespace multithreadingapplication { ? ? class threadcreationprogram ? ? { ? ? ? ? public static void calltochildthread() ? ? ? ? { ? ? ? ? ? ? console.writeline("child thread starts"); ? ? ? ? ? ? // 線程暫停 5000 毫秒 ? ? ? ? ? ? int sleepfor = 5000; ? ? ? ? ? ? console.writeline("child thread paused for {0} seconds", ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? sleepfor / 1000); ? ? ? ? ? ? thread.sleep(sleepfor); ? ? ? ? ? ? console.writeline("child thread resumes"); ? ? ? ? } ? ? ? ? ? ? ? ? static void main(string[] args) ? ? ? ? { ? ? ? ? ? ? threadstart childref = new threadstart(calltochildthread); ? ? ? ? ? ? console.writeline("in main: creating the child thread"); ? ? ? ? ? ? thread childthread = new thread(childref); ? ? ? ? ? ? childthread.start(); ? ? ? ? ? ? console.readkey(); ? ? ? ? } ? ? } }
當上面的代碼被編譯和執行時,它會產生下列結果:
in main: creating the child thread child thread starts child thread paused for 5 seconds child thread resumes
5. 銷毀線程
abort() 方法用于銷毀線程。
通過拋出 threadabortexception 在運行時中止線程。這個異常不能被捕獲,如果有 finally 塊,控制會被送至 finally 塊。
下面的程序說明了這點:
using system; using system.threading; namespace multithreadingapplication { ? ? class threadcreationprogram ? ? { ? ? ? ? public static void calltochildthread() ? ? ? ? { ? ? ? ? ? ? try ? ? ? ? ? ? { ? ? ? ? ? ? ? ? console.writeline("child thread starts"); ? ? ? ? ? ? ? ? // 計數到 10 ? ? ? ? ? ? ? ? for (int counter = 0; counter <= 10; counter++) ? ? ? ? ? ? ? ? { ? ? ? ? ? ? ? ? ? ? thread.sleep(500); ? ? ? ? ? ? ? ? ? ? console.writeline(counter); ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? console.writeline("child thread completed"); ? ? ? ? ? ? } ? ? ? ? ? ? catch (threadabortexception e) ? ? ? ? ? ? { ? ? ? ? ? ? ? ? console.writeline("thread abort exception"); ? ? ? ? ? ? } ? ? ? ? ? ? finally ? ? ? ? ? ? { ? ? ? ? ? ? ? ? console.writeline("couldn't catch the thread exception"); ? ? ? ? ? ? } ? ? ? ? } ? ? ? ? ? ? ? ? static void main(string[] args) ? ? ? ? { ? ? ? ? ? ? threadstart childref = new threadstart(calltochildthread); ? ? ? ? ? ? console.writeline("in main: creating the child thread"); ? ? ? ? ? ? thread childthread = new thread(childref); ? ? ? ? ? ? childthread.start(); ? ? ? ? ? ? // 停止主線程一段時間 ? ? ? ? ? ? thread.sleep(2000); ? ? ? ? ? ? // 現在中止子線程 ? ? ? ? ? ? console.writeline("in main: aborting the child thread"); ? ? ? ? ? ? childthread.abort(); ? ? ? ? ? ? console.readkey(); ? ? ? ? } ? ? } }
當上面的代碼被編譯和執行時,它會產生下列結果:
in main: creating the child thread child thread starts 0 1 2 in main: aborting the child thread thread abort exception couldn't catch the thread exception