C# 多線程

2022-09-14 14:11 更新

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

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

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


線程生命周期

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

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

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

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

主線程

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

當 C# 程序開始執(zhí)行時,主線程自動創(chuàng)建。使用 Thread 類創(chuàng)建的線程被主線程的子線程調(diào)用。您可以使用 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í)行時,它會產(chǎn)生下列結(jié)果:

This is MainThread

Thread 類常用的屬性和方法

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

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

下表列出了 Thread 類的一些常用的 方法

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

讀取字段值。無論處理器的數(shù)目或處理器緩存的狀態(tài)如何,該值都是由計算機的任何處理器寫入的最新值。此方法有不同的重載形式。這里只給出了一些形式。
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()
導(dǎo)致調(diào)用線程執(zhí)行準備好在當前處理器上運行的另一個線程。由操作系統(tǒng)選擇要執(zhí)行的線程。

創(chuàng)建線程

線程是通過擴展 Thread 類創(chuàng)建的。擴展的 Thread 類調(diào)用 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í)行時,它會產(chǎn)生下列結(jié)果:

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í)行時,它會產(chǎn)生下列結(jié)果:

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");
                // 計數(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();
            // 停止主線程一段時間
            Thread.Sleep(2000);             // 現(xiàn)在中止子線程
            Console.WriteLine("In Main: Aborting the Child thread");
            childThread.Abort();
            Console.ReadKey();
        }
    }
}

當上面的代碼被編譯和執(zhí)行時,它會產(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)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號