C# 多線程

2018-09-27 16:32 更新

多線程

線程的定義是程序的執(zhí)行路徑。每個線程都定義了一個獨特的控制流,如果應用程序涉及到復雜且耗時的操作,那么設置不同的線程執(zhí)行路徑會非常有好處,因為每個線程會被指定于執(zhí)行特定的工作。

線程實際上是輕量級進程。一個常見的使用線程的實例是現代操作系統(tǒng)中的并行編程。使用線程不僅有效地減少了 CPU 周期的浪費,同時還提高了應用程序的運行效率。

到目前為止我們所編寫的程序都是以一個單線程作為應用程序的運行的,其運行過程均為單一的。但是,在這種情況下,應用程序在同一時間只能執(zhí)行一個任務。為了使應用程序可以同時執(zhí)行多個任務,需要將其被劃分為多個更小的線程。

線程的聲明周期

線程的生命周期開始于對象的 System.Threading.Thread 類創(chuàng)建時,結束于線程被終止或是完成執(zhí)行時。下列各項為線程在生命周期中的各種狀態(tài):

  • 未啟動狀態(tài):線程實例已被創(chuàng)建但 Start 方法仍未被調用時的狀態(tài)。
  • 就緒狀態(tài):線程已準備好運行,正在等待 CPU 周期時的狀態(tài)。
  • 不可運行狀態(tài):下面的幾種情況下線程是不可運行的:
    • 已經調用 Sleep 方法
    • 已經調用 Wait 方法
    • 通過 I/O 操作阻塞
  • 死亡狀態(tài):線程已完成執(zhí)行或已終止的狀態(tài)。

主線程

在 C# 中,System.Threading.Thread 類用于線程的工作。它允許創(chuàng)建并訪問多線程應用程序中的單個線程。進程中第一個被執(zhí)行的線程稱為主線程。

當 C# 程序開始執(zhí)行時,會自動創(chuàng)建主線程。使用 Thread 類創(chuàng)建的線程被主線程的子線程調用。通過 Thread 類的 CurrentThread 屬性可以訪問線程。

下面的程序演示了主線程的執(zhí)行:

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();
      }
   }
}

編譯執(zhí)行上述代碼,得到如下結果:

This is MainThread

Thread 類的屬性和方法

下表為 Thread 類一些常用的屬性:

屬性描述
CurrentContext獲取線程當前正在執(zhí)行的線程的上下文。
CurrentCulture獲取或設置當前線程的區(qū)域性
CurrentPrinciple獲取或設置線程的當前責任人(針對基于角色的安全性)
CurrentThread獲取當前正在運行的線程
CurrentUICulture獲取或設置資源管理器當前所使用的區(qū)域性,便于在運行時查找區(qū)域性特定的資源
ExecutionContext獲取一個 ExcutionContext 對象,該對象包含有關當前線程的各種上下文信息。
IsAlive獲取一個值,指示當前線程的執(zhí)行狀態(tài)。
IsBackground獲取或設置一個值,指示線程是否為后臺線程。
IsThreadPoolThread獲取或設置一個值,指示線程是否屬于托管線程池。
ManagedThreadId獲取當前托管線程的唯一標識符
Name獲取或設置線程的名稱。
Priority獲取或設置一個值,指示線程的調度優(yōu)先級
ThreadState獲取一個值,指示當前線程的狀態(tài)。

下表為 Thread 類一些常用的方法:

序號方法名和描述
1public void Abort()
在調用此方法的線程上引發(fā) ThreadAbortException,則觸發(fā)終止此線程的操作。調用此方法通常會終止線程。
2public static LocalDataStoreSlot AllocateDataSlot()
在所有的線程上分配未命名的數據槽。使用以 ThreadStaticAttribute 屬性標記的字段,可獲得更好的性能。
3public static LocalDataStoreSlot AllocateNamedDataSlot( string name)
在所有線程上分配已命名的數據槽。使用以 ThreadStaticAttribute 屬性標記的字段,可獲得更好的性能。
4public static void BeginCriticalRegion()
通知主機將要進入一個代碼區(qū)域,若該代碼區(qū)域內的線程終止或發(fā)生未經處理的異常,可能會危害應用程序域中的其他任務。
5public static void BeginThreadAffinity()
通知主機托管代碼將要執(zhí)行依賴于當前物理操作系統(tǒng)線程的標識的指令。
6public static void EndCriticalRegion()
通知主機將要進入一個代碼區(qū)域,若該代碼區(qū)域內的線程終止或發(fā)送未經處理的異常,僅會影響當前任務。
7public static void EndThreadAffinity()
通知主機托管代碼已執(zhí)行完依賴于當前物理操作系統(tǒng)線程的標識的指令。
8public static void FreeNamedDataSlot(string name)
消除進程中所有線程的名稱與槽之間的關聯。使用以 ThreadStaticAttribute 屬性標記的字段,可獲得更好的性能。
9public static Object GetData( LocalDataStoreSlot slot )
在當前線程的當前域中從當前線程上指定的槽中檢索值。使用以 ThreadStaticAttribute 屬性標記的字段,可獲得更好的性能。
10public static AppDomain GetDomain()
返回當前線程正在其中運行的當前域。
11public static AppDomain GetDomainID()
返回唯一的應用程序域標識符。
12public static LocalDataStoreSlot GetNamedDataSlot( string name )
查找已命名的數據槽。使用以 ThreadStaticAttribute 屬性標記的字段,可獲得更好的性能。
13public void Interrupt()
中斷處于 WaitSleepJoin 線程狀態(tài)的線程。
14public void Join()
在繼續(xù)執(zhí)行標準的 COM 和 SendMessage 消息泵處理期間,阻塞調用線程,直到某個線程終止為止。此方法有不同的重載形式。
15public static void MemoryBarrier()
按如下方式同步內存存取:執(zhí)行當前線程的處理器在對指令進行重新排序時,不能采用先執(zhí)行 MemoryBarrier 調用之后的內存存取,再執(zhí)行 MemoryBarrier 調用之前的內存存取的方式。
16public static void ResetAbort()
取消當前線程請求的 Abort 操作。
17public static void SetData( LocalDataStoreSlot slot, Object data )
在指定槽中,設置當前正在運行中線程的當前域的數據。使用以 ThreadStaticAttribute 屬性標記的字段,可獲得更好的性能。
18public void Start()
開始一個線程。
19public static void Sleep( int millisecondsTimeout )
令線程暫停一段時間。
20public static void SpinWait( int iterations )
令線程等待一段時間,時間長度由 iterations 參數定義。
21public 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 )
讀取字段的值。無論處理器的數目或處理器緩存的狀態(tài)如何,該值都表示由計算機中任何一個處理器寫入的最新值。此方法有不同的重載形式,此處僅給出部分例子。
22public 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 )
立即寫入一個值到字段中,使該值對計算機中的所有處理器都可見。此方法有不同的重載形式,此處僅給出部分例子。
23public static bool Yield()
令調用線程執(zhí)行已準備好在當前處理器上運行的另一個線程,由操作系統(tǒng)選擇要執(zhí)行的線程。

線程的創(chuàng)建

線程是通過擴展 Thread 類創(chuàng)建的,擴展而來的 Thread 類調用 Start() 方法即可開始子線程的執(zhí)行。示例:

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();
      }
   }
}

編譯執(zhí)行上述代碼段,得到如下結果:

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();
      }
   }
}

編譯執(zhí)行上述代碼,得到如下代碼段:

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");

            // 執(zhí)行一些任務,如計十個數
            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();
      }
   }
}

編譯執(zhí)行上述代碼,得到如下結果:

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
以上內容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號