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 類中一些最常用的屬性

序號 屬性 & 描述
1

CurrentContext

獲取執行緒正在執行的當前上下文。

2

CurrentCulture

獲取或設定當前執行緒的區域性。

3

CurrentPrinciple

獲取或設定執行緒的當前主體(用於基於角色的安全)。

4

CurrentThread

獲取當前正在執行的執行緒。

5

CurrentUICulture

獲取或設定資源管理器在執行時查詢特定於區域性的資源時使用的當前區域性。

6

ExecutionContext

獲取包含當前執行緒各種上下文資訊的 ExecutionContext 物件。

7

IsAlive

獲取一個值,指示當前執行緒的執行狀態。

8

IsBackground

獲取或設定一個值,指示執行緒是否為後臺執行緒。

9

IsThreadPoolThread

獲取一個值,指示執行緒是否屬於託管執行緒池。

10

ManagedThreadId

獲取當前託管執行緒的唯一識別符號。

11

Name

獲取或設定執行緒的名稱。

12

Priority

獲取或設定一個值,指示執行緒的排程優先順序。

13

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");
         
         // the thread is paused for 5000 milliseconds
         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塊(如果有)。

以下程式對此進行了說明:

using System;
using System.Threading;

namespace MultithreadingApplication {
   class ThreadCreationProgram {
      public static void CallToChildThread() {
         try {
            Console.WriteLine("Child thread starts");
            
            // do some work, like counting to 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();
         
         //stop the main thread for some time
         Thread.Sleep(2000);
         
         //now abort the child
         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 
廣告