多線程基礎-.NET

多線程

多線程基礎-.NET


  • 線程 被定義為程序的執行流。每個線程都定義了一個單獨的控制流。如果應用程序涉及到複雜的和耗時的操作,那麼將程序分成多個線程執行流往往是有益的,每個線程執行特定的工作。
  • 線程是輕量級進程。一個使用線程的常見實例是現代操作系統中並行編程的實現。使用線程節省了 CPU 週期的浪費,同時提高了應用程序的效率。
  • 為了同時執行多個任務,一個程序可以被劃分為更小的線程。

線程生命週期

線程生命週期開始於 System.Threading.Thread 類的對象被創建時,結束於線程被終止或完成執行時。

  • 未啟動狀態:當線程實例被創建但Start方法未被調用時的狀況。
  • 就緒狀態:當調用了Start,線程準備好運行並等待 CPU 週期時的狀況。
  • 不可運行狀態:下面的幾種情況下線程是不可運行的:
  • 已經調用 Sleep 方法
  • 已經調用 Wait 方法
  • 通過 I/O 操作阻塞
  • 死亡狀態:當線程已完成執行或已中止時的狀況。

主線程

  • 在 C# 中,System.Threading.Thread 類用於線程的工作。它允許創建並訪問多線程應用程序中的單個線程。進程中第一個被執行的線程稱為主線程。
  • 當 C# 程序開始執行時,主線程自動創建。使用 Thread 類創建的線程被主線程的子線程調用。您可以使用 Thread 類的 CurrentThread 屬性訪問線程。

創建線程

構造函數

#ThreadStart:
#public delegate void ThreadStart();//一個無參數,無返回值的委託
#ParameterizedThreadStart:
#public delegate void ParameterizedThreadStart(object obj);//一個有一個參數、但無返回值的委託
#maxStackSize:線程要使用的最大堆棧大小(以字節為單位);如果為 0 則使用可執行文件的文件頭中指定的默認最大堆棧大小。 重要地,對於部分受信任的代碼,如果 maxStackSize 大於默認堆棧大小,則將其忽略。 不引發異常。

public Thread(ThreadStart start);
public Thread(ParameterizedThreadStart start);
public Thread(ThreadStart start, int maxStackSize);
public Thread(ParameterizedThreadStart start, int maxStackSize);

新建線程

public static void CallToChildThread()
{
Console.WriteLine("Child thread starts");
}
ThreadStart childref = new ThreadStart(CallToChildThread);
Thread childThread = new Thread(childref);
childThread.Start();

調用Start之後,一個線程就進入了就緒狀態了,等著CPU分配時間片即可。

以上,就是C#的多線程基本知識,如果你只是需要偶爾在程序中使用以下多線程,那麼上面的知識就夠你啟動一個子線程,並將想要執行的任務放到子線程中執行了。

但如果你在線程中大量使用多線程,那麼就可能遇到諸如:線程同步、死鎖、控制線程狀態等需求。


前後線程&後臺線程

#獲取或設置一個值,該值指示某個線程是否為後臺線程。
public bool IsBackground { get; set; }

.Net的公用語言運行時(Common Language Runtime,CLR)能區分兩種不同類型的線程:前臺線程和後臺線程。

這兩者的區別就是:

  • 應用程序必須運行完所有的前臺線程才可以退出;
  • 對於後臺線程,應用程序則可以不考慮其是否已經運行完畢而直接退出,所有的後臺線程在應用程序退出時都會自動結束。

.NET環境使用Thread建立的線程默認情況下是前臺線程,即線程屬性IsBackground=false,在進程中,只要有一個前臺線程未退出,進程就不會終止。主線程就是一個前臺線程。而後臺線程不管線程是否結束,只要所有的前臺線程都退出(包括正常退出和異常退出)後,進程就會自動終止。

一般後臺線程用於處理時間較短的任務,如在一個Web服務器中可以利用後臺線程來處理客戶端發過來的請求信息。而前臺線程一般用於處理需要長時間等待的任務,如在Web服務器中的監聽客戶端請求的程序,或是定時對某些系統資源進行掃描的程序。

  • 應用程序的主線程以及使用Thread構造的線程都默認為前臺線程
  • 線程池線程也就是使用 ThreadPool.QueueUserWorkItem()和Task工廠創建的線程都默認為後臺線程

線程執行狀態(IsAlive)

#獲取一個值,該值指示當前線程的執行狀態。
#如果此線程已啟動並且尚未正常終止或中止,則為 true;否則為 false。
public bool IsAlive { get; }

線程狀態(ThreadState)

#獲取一個值,該值包含當前線程的狀態
#System.Threading.ThreadState 值之一(Running、StopRequested、SuspendRequested、Background、Unstarted、Stopped、WaitSleepJoin、Suspended、AbortRequested、Aborted),它指示當前線程的狀態。 初始值為 Unstarted。
public ThreadState ThreadState { get; }

線程歸屬(IsThreadPoolThread)

#獲取一個值,該值指示線程是否屬於託管線程池。
#如果此線程屬於託管線程池,則為 true;否則為 false。
public bool IsThreadPoolThread { get; }

線程標識符(ManagedThreadId)

#獲取當前託管線程的唯一標識符。
#一個整數,表示此託管線程的唯一標識符。
public int ManagedThreadId { get; }

線程名字(Name)

#獲取或設置線程的名稱。
#包含線程名稱的字符串,或者如果未設置名稱,則為 null。
public string Name { get; set; }

線程優先級(Priority)

#獲取或設置一個值,該值指示線程的調度優先級。
#System.Threading.ThreadPriority(Lowest、BelowNormal、Normal、AboveNormal、Highest) 值之一。 默認值為 Normal。
public ThreadPriority Priority { get; set; }

立即執行當前線程,同時阻塞其餘線程

#在繼續執行標準的 COM 和 SendMessage 消息泵處理期間,阻塞調用線程,直到某個線程終止或經過了指定時間為止。
#如果線程已終止,則為 true;如果線程在經過了 millisecondsTimeout 參數指定的時間量後未終止,則為 false。

public void Join();
public bool Join(int millisecondsTimeout);
public bool Join(TimeSpan timeout);

這個方法可以使當前線程立即執行,如果沒有參數,則會一直等到當前線程執行結束,如果有參數,則當過時間之後,就會取消這種超級優先級狀態。

掛起線程與繼續掛起線程

#掛起線程,或者如果線程已掛起,則不起作用。
public void Suspend();
#繼續已掛起的線程。
public void Resume();

線程休眠(sleep)

#timeout:設置為線程被阻塞的時間量的 System.TimeSpan。 指定零以指示應掛起此線程以使其他等待線程能夠執行。 指定 System.Threading.Timeout.Infinite以無限期阻止線程。
public static void Sleep(TimeSpan timeout);
#線程被阻塞的毫秒數。 指定零 (0) 以指示應掛起此線程以使其他等待線程能夠執行。 指定 System.Threading.Timeout.Infinite以無限期阻止線程。
public static void Sleep(int millisecondsTimeout);
#當前線程暫停 5000 毫秒
int sleepfor = 5000;
Thread.Sleep(sleepfor);

銷燬線程(Abort)

  • Abort() 方法用於銷燬線程。
  • 通過拋出 threadabortexception 在運行時中止線程。這個異常不能被捕獲,如果有 finally 塊,控制會被送至 finally 塊。
#在調用此方法的線程上引發 System.Threading.ThreadAbortException,以開始終止此線程的過程。 調用此方法通常會終止線程。
public void Abort(object stateInfo);
public void Abort();
public static void CallToChildThread()
{
try{
Console.WriteLine("Child thread starts");
// 計數到 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);
// 現在中止子線程
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 Exceptio




分享到:


相關文章: