本筆記摘抄自:https://www.cnblogs.com/zhili/archive/2012/07/18/Thread.html,記錄一下學習程序以備后續查用,
一、執行緒的介紹
行程(Process)是應用程式的實體要使用的資源的一個集合,每個應用程式都在各自的行程中運行來確保應用程式不受其他應用程式的影響,
執行緒是行程中基本執行單元, 一個行程中可以包含多個執行緒,在行程入口執行的第一個執行緒是一個行程的主執行緒,在.NET應用程式中,都是以Main()方法
作為程式的入口(執行緒是行程的執行單元,行程是執行緒的一個容器),
二、執行緒調度和優先級
Windows之所以被稱為搶占式多執行緒作業系統,是因為執行緒可以在任意時間被搶占,并調度另一個執行緒,
每個執行緒都分配了從0~31的一個優先級,系統首先把高優先級的執行緒分配給CPU執行,
Windows 支持7個相對執行緒優先級:Idle、Lowest、Below Normal、Normal、Above Normal、Highest和Time-Critical,Normal是默認的執行緒優先級,
然而在程式中可以通過設定Thread的Priority屬性來改變執行緒的優先級,它的型別為ThreadPriority列舉型別:Lowest、BelowNormal、Normal、AboveNormal
和Highest,CLR為自己保留了 Idle和Time-Critical優先級,
列舉值串列如下:
| 成員名稱 | 說明 |
| Lowest | 可以將Thread置于其他優先級執行緒之后, |
| BelowNormal | 可以將Thread置于Normal優先級執行緒之后Lowest優先級執行緒之前, |
| Normal |
可以將Thread置于AboveNormal優先級執行緒之后BelowNormal優先級執行緒之前, 默認情況下,執行緒置于Normal優先級, |
| AboveNormal | 可以將Thread置于Highest優先級執行緒之后Normal優先級執行緒之前, |
| Highest | 可以將Thread置于其他優先級執行緒之前, |
三、前臺執行緒和后臺執行緒
在.NET中執行緒分為前臺執行緒和后臺執行緒:
1、主執行緒是程式開始時就執行的,如果你需要再創建執行緒,那么創建的執行緒就是這個主執行緒的子執行緒,它是前臺執行緒,
2、子執行緒可以是前臺執行緒也可以是后臺執行緒,
3、前臺執行緒必須全部執行完,即使主執行緒關閉掉,這時行程仍然存活,
4、當所有前臺執行緒停止運行時,CLR會強制結束仍在運行的任何后臺執行緒,這些后臺執行緒直接被終止,不會拋出例外,
5、前臺執行緒與后臺執行緒唯一的區別是后臺執行緒不會阻止行程終止,可以在任何時候將前臺執行緒修改為后臺執行緒,
static void Main(string[] args) { ThreadType(); } /// <summary> /// 前臺執行緒與后臺執行緒 /// </summary> private static void ThreadType() { //創建一個新執行緒(默認為前臺執行緒) Thread backThread = new Thread(Worker) { //將執行緒更改為后臺執行緒 IsBackground = true }; //通過Start方法啟動執行緒 backThread.Start(); //如果backThread是前臺執行緒,則應用程式5秒后才終止, //如果backThread是后臺執行緒,則應用程式立即終止, Console.WriteLine("It's from main thread."); //Console.Read(); } private static void Worker() { //休息5秒 Thread.Sleep(5000); Console.WriteLine("It's from worker thread."); }
假如保留IsBackground = true;但又想繼續執行Worker()方法的話,可以呼叫Thread.Join()方法來實作,Join()方法能保證主執行緒(前臺執行緒)在異步執行緒
Thread(后臺執行緒)運行結束后才會運行,
注1:一個執行緒在執行的程序中,可能呼叫另一個執行緒,前者可以稱為呼叫執行緒,后者成為被呼叫執行緒,
注2:Thread.Join()方法的使用場景:呼叫執行緒掛起,等待被呼叫執行緒執行完畢后,繼續執行,
注3:被呼叫執行緒執行Join方法,告訴呼叫執行緒,你先暫停,我執行完了,你再執行,從而保證了先后關系,
static void Main(string[] args) { ThreadStatusChange(); } /// <summary> /// 執行緒狀態之間的轉換 /// </summary> private static void ThreadStatusChange() { //創建一個新執行緒(默認為前臺執行緒) Thread backThread = new Thread(Worker) { //將執行緒更改為后臺執行緒 IsBackground = true }; //通過Start方法啟動執行緒 backThread.Start(); //Join()方法能保證主執行緒(前臺執行緒)在異步執行緒Thread(后臺執行緒)運行結束后才會運行 backThread.Join(); Console.WriteLine("It's from main thread."); Console.Read(); } private static void Worker() { //休息5秒 Thread.Sleep(5000); Console.WriteLine("It's from worker thread."); }
運行結果如下:

四、 Suspend和Resume方法
這兩個方法在.NET Framework 1.0的時候就支持的方法,他們分別可以掛起執行緒及恢復掛起的執行緒,但在.NET Framework 2.0以后的版本中這兩個方法都過時了,
MSDN的解釋是這樣:
警告:
不要使用Suspend和Resume方法來同步執行緒的活動,您無法知道掛起執行緒時它正在執行什么代碼,如果您在安全權限評估期間掛起持有鎖的執行緒,
則AppDomain中的其他執行緒可能被阻止,如果您在執行緒正在執行類建構式時掛起它,則 AppDomain中嘗試使用該類的其他執行緒將被阻止,這樣很容易發生死鎖,
static void Main(string[] args) { ThreadResume(); } /// <summary> /// 執行緒恢復 /// </summary> private static void ThreadResume() { Thread thread = new Thread(ThreadSuspend) { Name = "Thread1" }; thread.Start(); Thread.Sleep(2000); Console.WriteLine("Main Thread is running."); //執行緒恢復 thread.Resume(); Console.Read(); } /// <summary> /// 執行緒掛起 /// </summary> private static void ThreadSuspend() { Console.WriteLine("Thread: {0} has been suspended.", Thread.CurrentThread.Name); //將當前執行緒掛起 Thread.CurrentThread.Suspend(); Console.WriteLine("Thread: {0} has been resumed.", Thread.CurrentThread.Name); }
在上面這段代碼中Thread1執行緒是在主執行緒中恢復的,但當主執行緒發生例外時,這時候Thread1就會一直處于掛起狀態,此時Thread1所使用的資源就不能釋放
(除非強制終止行程),當其它的執行緒需要使用這快資源的時候, 很有可能就會發生死鎖現象,
上面一段代碼還存在一個隱患,假如把Thread.Sleep(2000);這段代碼注釋一下:
static void Main(string[] args) { ThreadResume(); } /// <summary> /// 執行緒恢復 /// </summary> private static void ThreadResume() { Thread thread = new Thread(ThreadSuspend) { Name = "Thread1" }; thread.Start(); //Thread.Sleep(2000); Console.WriteLine("Main Thread is running."); //執行緒恢復 thread.Resume(); Console.Read(); } /// <summary> /// 執行緒掛起 /// </summary> private static void ThreadSuspend() { Console.WriteLine("Thread: {0} has been suspended.", Thread.CurrentThread.Name); //將當前執行緒掛起 Thread.CurrentThread.Suspend(); Console.WriteLine("Thread: {0} has been resumed.", Thread.CurrentThread.Name); }
這個時候,主執行緒因為跑(運行)得太快,做完自己的事情去喚醒Thread1時,此時Thread1還沒有掛起,而此時喚醒Thread1就會出現例外了,
五、Abort和Interrupt方法
Abort方法和Interrupt都是用來終止執行緒的,但是兩者還是有區別的:
1、它們拋出的例外不一樣:Abort 方法拋出的例外是ThreadAbortException,Interrupt拋出的例外為ThreadInterruptedException,
2、呼叫Interrupt方法的執行緒之后可以被喚醒,然而呼叫Abort方法的執行緒就直接被終止不能被喚醒了,
下面演示Abort方法的使用:
static void Main(string[] args) { //ThreadType(); //ThreadStatusChange(); //ThreadResume(); ThreadAbort(); } /// <summary> /// 執行緒中斷(不可再喚醒) /// </summary> private static void ThreadAbort() { Thread threadAbort = new Thread(AbortMethod) { Name = "ThreadAbort" }; threadAbort.Start(); Thread.Sleep(1000); try { threadAbort.Abort(); } catch { Console.WriteLine("1-> {0} exception happen in main thread.", Thread.CurrentThread.Name); Console.WriteLine("2-> {0} status is:{1} in main thread.", Thread.CurrentThread.Name, Thread.CurrentThread.ThreadState); } finally { Console.WriteLine("3-> {0} status is:{1} in main thread.", threadAbort.Name, threadAbort.ThreadState); } threadAbort.Join(); Console.WriteLine("4-> {0} status is:{1}", threadAbort.Name, threadAbort.ThreadState); Console.Read(); } /// <summary> /// Abort方法 /// </summary> private static void AbortMethod() { try { Thread.Sleep(5000); } catch (Exception e) { Console.WriteLine(e.GetType().Name); Console.WriteLine("5-> {0} exception happen in abort thread.", Thread.CurrentThread.Name); Console.WriteLine("6-> {0} status is:{1} in abort thread.", Thread.CurrentThread.Name, Thread.CurrentThread.ThreadState); } finally { Console.WriteLine("7-> {0} status is:{1} in abort thread.", Thread.CurrentThread.Name, Thread.CurrentThread.ThreadState); } }
運行結果如下:

從運行結果可以看出,呼叫Abort方法的執行緒引發的例外型別為ThreadAbortException,另外例外只會在呼叫Abort方法的執行緒中發生,而不會在主執行緒中拋出,
其次呼叫Abort方法后執行緒的狀態不是立即改變為Aborted狀態,而是從AbortRequested->Aborted,
下面演示Interrupt方法的使用:
static void Main(string[] args) { ThreadInterrupt(); } /// <summary> /// 執行緒中斷(可再喚醒) /// </summary> static void ThreadInterrupt() { Thread threadInterrupt = new Thread(InterruptMethod) { Name = "ThreadInterrupt" }; threadInterrupt.Start(); threadInterrupt.Interrupt(); threadInterrupt.Join(); Console.WriteLine("1-> {0} status is:{1} ", threadInterrupt.Name, threadInterrupt.ThreadState); Console.Read(); } /// <summary> /// Interrupt方法 /// </summary> private static void InterruptMethod() { try { Thread.Sleep(5000); } catch (Exception e) { Console.WriteLine(e.GetType().Name); Console.WriteLine("2-> {0} exception happen in interrupt thread.", Thread.CurrentThread.Name); Console.WriteLine("3-> {0} status is:{1} in interrupt thread.", Thread.CurrentThread.Name, Thread.CurrentThread.ThreadState); } finally { Console.WriteLine("4-> {0} status is:{1} in interrupt thread.", Thread.CurrentThread.Name, Thread.CurrentThread.ThreadState); } }
運行結果如下:

從結果中可以得到,呼叫Interrupt方法拋出的例外為:ThreadInterruptException, 另外當呼叫Interrupt方法后執行緒的狀態應該是中斷的,但是從運行結果看,
此時的執行緒因為Join、Sleep方法而喚醒了執行緒,
為了進一步解釋呼叫Interrupt方法的執行緒可以被喚醒, 我們可以在執行緒執行的方法中運用回圈,如果執行緒可以喚醒,則輸出結果中就一定會有回圈的部分,
而呼叫Abort方法的執行緒則不會有回圈的部分,
下面代碼相信大家看后肯定會更加理解兩個方法的區別:
static void Main(string[] args) { //ThreadType(); //ThreadStatusChange(); //ThreadResume(); //ThreadAbort(); //ThreadInterrupt(); ThreadWake(); } /// <summary> /// 執行緒喚醒 /// </summary> static void ThreadWake() { Thread threadWake = new Thread(WakeMethod); threadWake.Start(); Thread.Sleep(100); threadWake.Interrupt(); Thread.Sleep(3000); Console.WriteLine("1-> After finally block,the threadWake status is:{0}", threadWake.ThreadState); Console.Read(); } /// <summary> /// Wake方法 /// </summary> private static void WakeMethod() { for (int i = 0; i < 4; i++) { try { Thread.Sleep(2000); Console.WriteLine("2-> Thread is Running."); } catch (Exception ex) { if (ex != null) { Console.WriteLine("3-> Exception {0} throw.", ex.GetType().Name); } } finally { Console.WriteLine("4-> Current thread status is:{0}", Thread.CurrentThread.ThreadState); } } }
運行結果如下:

如果把上面的threadWake.Interrupt();改為threadWake.Abort(); 運行結果為:

六、簡單執行緒的使用
其實在上面介紹前臺執行緒和后臺執行緒的時候已經通過ThreadStart委托創建一個執行緒了,此時已經實作了一個多執行緒的一個程序,
下面通過ParameterizedThreadStart委托的方式來實作多執行緒:
static void Main(string[] args) { ThreadTypeUseParameterized(); } /// <summary> /// 前臺執行緒與后臺執行緒(使用ParameterizedThreadStart委托的方式來實作多執行緒) /// </summary> private static void ThreadTypeUseParameterized() { //創建一個新執行緒(默認為前臺執行緒) Thread backThread = new Thread(new ParameterizedThreadStart(Worker1)); //通過Start方法啟動執行緒 backThread.Start(123); //如果backThread是前臺執行緒,則應用程式5秒后才終止, //如果backThread是后臺執行緒,則應用程式立即終止, Console.WriteLine("It's from main thread."); } private static void Worker1(object parameter) { //休息5秒 Thread.Sleep(5000); Console.WriteLine(parameter+" is from worker1 thread."); Console.Read(); }
運行結果如下:
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/97976.html
標籤:C#
