C# 多線程
線程 被定義為程式的執行路徑。每個線程都定義了一個獨特的控制流。如果您的應用程式涉及複雜的和耗時的操作,那麼設定不同的執行緒執行路徑往往是有益的,每個執行緒執行特定的工作。
執行緒是輕量級進程。一個使用執行緒的常見實例是現代作業系統中並行程式設計的實作。使用執行緒節省了 CPU 週期的浪費,同時提高了應用程式的效率。
到目前為止我們寫的程式是一個單一執行緒作為應用程式的運行實例的單一的過程運行的。但是,這樣子應用程式同時只能執行一個任務。為了同時執行多個任務,它可以被劃分為更小的執行緒。
執行緒生命週期
執行緒生命週期開始於 System.Threading.Thread 類別的物件被建立時,結束於執行緒被終止或完成執行時。
下面列出了執行緒生命週期中的各種狀態:
#未啟動狀態:當執行緒實例已建立但Start 方法未被調用時的狀況。
就緒狀態:當執行緒準備好運行並等待 CPU 週期時的狀況。
不可運行狀態:下面的幾種情況下執行緒是不可運行的:
已經呼叫Sleep 方法
已經呼叫Wait 方法
透過I/O 操作阻塞
#死亡狀態:當執行緒已完成執行或已中止時的狀況。
主執行緒
在 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
Thread 類別常用的屬性與方法
下表列出了Thread 類別的一些常用的屬性:
屬性 | 描述 |
---|---|
#CurrentContext | 取得執行緒正在其中執行的目前上下文。 |
CurrentCulture | 取得或設定目前執行緒的區域性。 |
CurrentPrinciple | 取得或設定執行緒的目前負責人(對基於角色的安全性而言)。 |
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() 導致呼叫執行緒執行準備好在目前處理器上執行的另一個執行緒。由作業系統選擇要執行的線程。 |
建立執行緒
執行緒是透過擴充 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
管理執行緒
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
銷毀執行緒
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