C# 多線程

2022-09-14 14:11 更新

線程 被定義為程序的執(zhí)行路徑。每個(gè)線程都定義了一個(gè)獨(dú)特的控制流。如果您的應(yīng)用程序涉及到復(fù)雜的和耗時(shí)的操作,那么設(shè)置不同的線程執(zhí)行路徑往往是有益的,每個(gè)線程執(zhí)行特定的工作。

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

到目前為止我們編寫的程序是一個(gè)單線程作為應(yīng)用程序的運(yùn)行實(shí)例的單一的過(guò)程運(yùn)行的。但是,這樣子應(yīng)用程序同時(shí)只能執(zhí)行一個(gè)任務(wù)。為了同時(shí)執(zhí)行多個(gè)任務(wù),它可以被劃分為更小的線程。


線程生命周期

線程生命周期開始于 System.Threading.Thread 類的對(duì)象被創(chuàng)建時(shí),結(jié)束于線程被終止或完成執(zhí)行時(shí)。

下面列出了線程生命周期中的各種狀態(tài):

  • 未啟動(dòng)狀態(tài):當(dāng)線程實(shí)例被創(chuàng)建但 Start 方法未被調(diào)用時(shí)的狀況。
  • 就緒狀態(tài):當(dāng)線程準(zhǔn)備好運(yùn)行并等待 CPU 周期時(shí)的狀況。
  • 不可運(yùn)行狀態(tài):下面的幾種情況下線程是不可運(yùn)行的:

    • 已經(jīng)調(diào)用 Sleep 方法
    • 已經(jīng)調(diào)用 Wait 方法
    • 通過(guò) I/O 操作阻塞
  • 死亡狀態(tài):當(dāng)線程已完成執(zhí)行或已中止時(shí)的狀況。

主線程

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

當(dāng) C# 程序開始執(zhí)行時(shí),主線程自動(dòng)創(chuàng)建。使用 Thread 類創(chuàng)建的線程被主線程的子線程調(diào)用。您可以使用 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();
        }
    }
}

當(dāng)上面的代碼被編譯和執(zhí)行時(shí),它會(huì)產(chǎn)生下列結(jié)果:

This is MainThread

Thread 類常用的屬性和方法

下表列出了 Thread 類的一些常用的 屬性

屬性 描述
CurrentContext 獲取線程正在其中執(zhí)行的當(dāng)前上下文。
CurrentCulture 獲取或設(shè)置當(dāng)前線程的區(qū)域性。
CurrentPrinciple 獲取或設(shè)置線程的當(dāng)前負(fù)責(zé)人(對(duì)基于角色的安全性而言)。
CurrentThread 獲取當(dāng)前正在運(yùn)行的線程。
CurrentUICulture 獲取或設(shè)置資源管理器使用的當(dāng)前區(qū)域性以便在運(yùn)行時(shí)查找區(qū)域性特定的資源。
ExecutionContext 獲取一個(gè) ExecutionContext 對(duì)象,該對(duì)象包含有關(guān)當(dāng)前線程的各種上下文的信息。
IsAlive 獲取一個(gè)值,該值指示當(dāng)前線程的執(zhí)行狀態(tài)。
IsBackground 獲取或設(shè)置一個(gè)值,該值指示某個(gè)線程是否為后臺(tái)線程。
IsThreadPoolThread 獲取一個(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) 方法名 & 描述
1 public void Abort()
在調(diào)用此方法的線程上引發(fā) ThreadAbortException,以開始終止此線程的過(guò)程。調(diào)用此方法通常會(huì)終止線程。
2 public static LocalDataStoreSlot AllocateDataSlot()
在所有的線程上分配未命名的數(shù)據(jù)槽。為了獲得更好的性能,請(qǐng)改用以 ThreadStaticAttribute 屬性標(biāo)記的字段。
3 public static LocalDataStoreSlot AllocateNamedDataSlot( string name)
在所有線程上分配已命名的數(shù)據(jù)槽。為了獲得更好的性能,請(qǐng)改用以 ThreadStaticAttribute 屬性標(biāo)記的字段。
4 public static void BeginCriticalRegion()
通知主機(jī)執(zhí)行將要進(jìn)入一個(gè)代碼區(qū)域,在該代碼區(qū)域內(nèi)線程中止或未經(jīng)處理的異常的影響可能會(huì)危害應(yīng)用程序域中的其他任務(wù)。
5 public static void BeginThreadAffinity()
通知主機(jī)托管代碼將要執(zhí)行依賴于當(dāng)前物理操作系統(tǒng)線程的標(biāo)識(shí)的指令。
6 public static void EndCriticalRegion()
通知主機(jī)執(zhí)行將要進(jìn)入一個(gè)代碼區(qū)域,在該代碼區(qū)域內(nèi)線程中止或未經(jīng)處理的異常僅影響當(dāng)前任務(wù)。
7 public static void EndThreadAffinity()
通知主機(jī)托管代碼已執(zhí)行完依賴于當(dāng)前物理操作系統(tǒng)線程的標(biāo)識(shí)的指令。
8 public static void FreeNamedDataSlot(string name)
為進(jìn)程中的所有線程消除名稱與槽之間的關(guān)聯(lián)。為了獲得更好的性能,請(qǐng)改用以 ThreadStaticAttribute 屬性標(biāo)記的字段。
9 public static Object GetData( LocalDataStoreSlot slot )
在當(dāng)前線程的當(dāng)前域中從當(dāng)前線程上指定的槽中檢索值。為了獲得更好的性能,請(qǐng)改用以 ThreadStaticAttribute 屬性標(biāo)記的字段。
10 public static AppDomain GetDomain()
返回當(dāng)前線程正在其中運(yùn)行的當(dāng)前域。
11 public static AppDomain GetDomainID()
返回唯一的應(yīng)用程序域標(biāo)識(shí)符。
12 public static LocalDataStoreSlot GetNamedDataSlot( string name )
查找已命名的數(shù)據(jù)槽。為了獲得更好的性能,請(qǐng)改用以 ThreadStaticAttribute 屬性標(biāo)記的字段。
13 public void Interrupt()
中斷處于 WaitSleepJoin 線程狀態(tài)的線程。
14 public void Join()
在繼續(xù)執(zhí)行標(biāo)準(zhǔn)的 COM 和 SendMessage 消息泵處理期間,阻塞調(diào)用線程,直到某個(gè)線程終止為止。此方法有不同的重載形式。
15 public static void MemoryBarrier()
按如下方式同步內(nèi)存存?。簣?zhí)行當(dāng)前線程的處理器在對(duì)指令重新排序時(shí),不能采用先執(zhí)行 MemoryBarrier 調(diào)用之后的內(nèi)存存取,再執(zhí)行 MemoryBarrier 調(diào)用之前的內(nèi)存存取的方式。
16 public static void ResetAbort()
取消為當(dāng)前線程請(qǐng)求的 Abort。
17 public static void SetData( LocalDataStoreSlot slot, Object data )
在當(dāng)前正在運(yùn)行的線程上為此線程的當(dāng)前域在指定槽中設(shè)置數(shù)據(jù)。為了獲得更好的性能,請(qǐng)改用以 ThreadStaticAttribute 屬性標(biāo)記的字段。
18 public void Start()
開始一個(gè)線程。
19 public static void Sleep( int millisecondsTimeout )
讓線程暫停一段時(shí)間。
20 public static void SpinWait( int iterations )
導(dǎo)致線程等待由 iterations 參數(shù)定義的時(shí)間量。
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 )

讀取字段值。無(wú)論處理器的數(shù)目或處理器緩存的狀態(tài)如何,該值都是由計(jì)算機(jī)的任何處理器寫入的最新值。此方法有不同的重載形式。這里只給出了一些形式。
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 )

立即向字段寫入一個(gè)值,以使該值對(duì)計(jì)算機(jī)中的所有處理器都可見。此方法有不同的重載形式。這里只給出了一些形式。
23 public static bool Yield()
導(dǎo)致調(diào)用線程執(zhí)行準(zhǔn)備好在當(dāng)前處理器上運(yùn)行的另一個(gè)線程。由操作系統(tǒng)選擇要執(zhí)行的線程。

創(chuàng)建線程

線程是通過(guò)擴(kuò)展 Thread 類創(chuàng)建的。擴(kuò)展的 Thread 類調(diào)用 Start() 方法來(lái)開始子線程的執(zhí)行。

下面的程序演示了這個(gè)概念:

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

當(dāng)上面的代碼被編譯和執(zhí)行時(shí),它會(huì)產(chǎn)生下列結(jié)果:

In Main: Creating the Child thread
Child thread starts

管理線程

Thread 類提供了各種管理線程的方法。

下面的實(shí)例演示了 sleep() 方法的使用,用于在一個(gè)特定的時(shí)間暫停線程。

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

當(dāng)上面的代碼被編譯和執(zhí)行時(shí),它會(huì)產(chǎn)生下列結(jié)果:

In Main: Creating the Child thread
Child thread starts
Child Thread Paused for 5 seconds
Child thread resumes

銷毀線程

Abort() 方法用于銷毀線程。

通過(guò)拋出 threadabortexception 在運(yùn)行時(shí)中止線程。這個(gè)異常不能被捕獲,如果有 finally 塊,控制會(huì)被送至 finally 塊。

下面的程序說(shuō)明了這點(diǎn):

using System;
using System.Threading;

namespace MultithreadingApplication{
    class ThreadCreationProgram{
        public static void CallToChildThread(){
            try{
                Console.WriteLine("Child thread starts");
                // 計(jì)數(shù)到 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();
            // 停止主線程一段時(shí)間
            Thread.Sleep(2000);             // 現(xiàn)在中止子線程
            Console.WriteLine("In Main: Aborting the Child thread");
            childThread.Abort();
            Console.ReadKey();
        }
    }
}

當(dāng)上面的代碼被編譯和執(zhí)行時(shí),它會(huì)產(chǎn)生下列結(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)