C# 多線程

2018-09-27 16:32 更新

多線程

線程的定義是程序的執(zhí)行路徑。每個(gè)線程都定義了一個(gè)獨(dú)特的控制流,如果應(yīng)用程序涉及到復(fù)雜且耗時(shí)的操作,那么設(shè)置不同的線程執(zhí)行路徑會(huì)非常有好處,因?yàn)槊總€(gè)線程會(huì)被指定于執(zhí)行特定的工作。

線程實(shí)際上是輕量級(jí)進(jìn)程。一個(gè)常見(jiàn)的使用線程的實(shí)例是現(xiàn)代操作系統(tǒng)中的并行編程。使用線程不僅有效地減少了 CPU 周期的浪費(fèi),同時(shí)還提高了應(yīng)用程序的運(yùn)行效率。

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

線程的聲明周期

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

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

主線程

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

當(dāng) C# 程序開(kāi)始執(zhí)行時(shí),會(huì)自動(dòng)創(chuàng)建主線程。使用 Thread 類創(chuàng)建的線程被主線程的子線程調(diào)用。通過(guò) Thread 類的 CurrentThread 屬性可以訪問(wèn)線程。

下面的程序演示了主線程的執(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í)行上述代碼,得到如下結(jié)果:

This is MainThread

Thread 類的屬性和方法

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

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

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

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

線程的創(chuàng)建

線程是通過(guò)擴(kuò)展 Thread 類創(chuàng)建的,擴(kuò)展而來(lái)的 Thread 類調(diào)用 Start() 方法即可開(kāi)始子線程的執(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í)行上述代碼段,得到如下結(jié)果:

In Main: Creating the Child thread
Child thread starts

線程的管理

Thread 類提供了多種用于線程管理的方法。下面的示例調(diào)用了 sleep() 方法來(lái)在一段特定時(shí)間內(nèi)暫停線程:

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() 方法可銷毀線程。

在運(yùn)行時(shí),通過(guò)拋出 ThreadAbortException 來(lái)終止線程。這個(gè)異常無(wú)法被捕獲,當(dāng)且僅當(dāng)具備 finally 塊時(shí),才將控制送到 finally 塊中。

示例:

using System;
using System.Threading;

namespace MultithreadingApplication
{
   class ThreadCreationProgram
   {
      public static void CallToChildThread()
      {
         try
         {
            Console.WriteLine("Child thread starts");

            // 執(zhí)行一些任務(wù),如計(jì)十個(gè)數(shù)
            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();

         // 將主線程停止一段時(shí)間
         Thread.Sleep(2000);

         // 中止子線程
         Console.WriteLine("In Main: Aborting the Child thread");

         childThread.Abort();
         Console.ReadKey();
      }
   }
}

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

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
以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)