<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
Thread.CurrentThread
是一個 靜態的 Thread 類,Thread 的CurrentThread
屬性,可以獲取到當前執行執行緒的一些資訊,其定義如下:
public static System.Threading.Thread CurrentThread { get; }
Thread 類有很多屬性和方法,這裡就不列舉了,後面的學習會慢慢熟悉更多 API 和深入瞭解使用。
這裡有一個簡單的範例:
static void Main(string[] args) { Thread thread = new Thread(OneTest); thread.Name = "Test"; thread.Start(); Console.ReadKey(); } public static void OneTest() { Thread thisTHread = Thread.CurrentThread; Console.WriteLine("執行緒標識:" + thisTHread.Name); Console.WriteLine("當前地域:" + thisTHread.CurrentCulture.Name); // 當前地域 Console.WriteLine("執行緒執行狀態:" + thisTHread.IsAlive); Console.WriteLine("是否為後臺執行緒:" + thisTHread.IsBackground); Console.WriteLine("是否為執行緒池執行緒"+thisTHread.IsThreadPoolThread); }
輸出
執行緒標識:Test 當前地域:zh-CN 執行緒執行狀態:True 是否為後臺執行緒:False 是否為執行緒池執行緒False
一般認為,執行緒有五種狀態:
新建(new 物件) 、就緒(等待CPU排程)、執行(CPU正在執行)、阻塞(等待阻塞、同步阻塞等)、死亡(物件釋放)。
理論的東西不說太多,直接擼程式碼。
新建執行緒簡直滾瓜爛熟,無非 new
一下,然後 Start()
。
Thread thread = new Thread();
Thread 的建構函式有四個:
public Thread(ParameterizedThreadStart start); public Thread(ThreadStart start); public Thread(ParameterizedThreadStart start, int maxStackSize); public Thread(ThreadStart start, int maxStackSize);
我們以啟動新的執行緒時傳遞引數來舉例,使用這四個建構函式呢?
ParameterizedThreadStart 是一個委託,建構函式傳遞的引數為需要執行的方法,然後在 Start
方法中傳遞引數。
需要注意的是,傳遞的引數型別為 object,而且只能傳遞一個。
程式碼範例如下:
static void Main(string[] args) { string myParam = "abcdef"; ParameterizedThreadStart parameterized = new ParameterizedThreadStart(OneTest); Thread thread = new Thread(parameterized); thread.Start(myParam); Console.ReadKey(); } public static void OneTest(object obj) { string str = obj as string; if (string.IsNullOrEmpty(str)) return; Console.WriteLine("新的執行緒已經啟動"); Console.WriteLine(str); }
此種方法不需要作為引數傳遞,各個執行緒共用堆疊。
優點是不需要裝箱拆箱,多執行緒可以共用空間;缺點是變數是大家都可以存取,此種方式在多執行緒競價時,可能會導致多種問題(可以加鎖解決)。
下面使用兩個變數實現資料傳遞:
class Program { private string A = "成員變數"; public static string B = "靜態變數"; static void Main(string[] args) { // 建立一個類 Program p = new Program(); Thread thread1 = new Thread(p.OneTest1); thread1.Name = "Test1"; thread1.Start(); Thread thread2 = new Thread(OneTest2); thread2.Name = "Test2"; thread2.Start(); Console.ReadKey(); } public void OneTest1() { Console.WriteLine("新的執行緒已經啟動"); Console.WriteLine(A); // 本身物件的其它成員 } public static void OneTest2() { Console.WriteLine("新的執行緒已經啟動"); Console.WriteLine(B); // 全域性靜態變數 } }
原理是 Thread 的建構函式 public Thread(ThreadStart start);
,ThreadStart
是一個委託,其定義如下
public delegate void ThreadStart();
使用委託的話,可以這樣寫
static void Main(string[] args) { System.Threading.ThreadStart start = DelegateThread; Thread thread = new Thread(start); thread.Name = "Test"; thread.Start(); Console.ReadKey(); } public static void DelegateThread() { OneTest("a", "b", 666, new Program()); } public static void OneTest(string a, string b, int c, Program p) { Console.WriteLine("新的執行緒已經啟動"); }
有那麼一點點麻煩,不過我們可以使用 Lambda 快速實現。
使用 Lambda 範例如下:
static void Main(string[] args) { Thread thread = new Thread(() => { OneTest("a", "b", 666, new Program()); }); thread.Name = "Test"; thread.Start(); Console.ReadKey(); } public static void OneTest(string a, string b, int c, Program p) { Console.WriteLine("新的執行緒已經啟動"); }
提示:如果需要處理的演演算法比較簡單的話,可以直接寫進委託中,不需要另外寫方法啦。
可以看到,C# 是多麼的方便。
Thread.Sleep()
方法可以將當前執行緒掛起一段時間,Thread.Join()
方法可以阻塞當前執行緒一直等待另一個執行緒執行至結束。
在等待執行緒 Sleep()
或 Join()
的過程中,執行緒是阻塞的(Blocket)。
阻塞的定義:當執行緒由於特點原因暫停執行,那麼它就是阻塞的。
如果執行緒處於阻塞狀態,執行緒就會交出他的 CPU 時間片,並且不會消耗 CPU 時間,直至阻塞結束。
阻塞會發生上下文切換。
程式碼範例如下:
static void Main(string[] args) { Thread thread = new Thread(OneTest); thread.Name = "小弟弟"; Console.WriteLine($"{DateTime.Now}:大家在吃飯,吃完飯後要帶小弟弟逛街"); Console.WriteLine("吃完飯了"); Console.WriteLine($"{DateTime.Now}:小弟弟開始玩遊戲"); thread.Start(); // 化妝 5 s Console.WriteLine("不管他,大姐姐化妝先"); Thread.Sleep(TimeSpan.FromSeconds(5)); Console.WriteLine($"{DateTime.Now}:化完妝,等小弟弟打完遊戲"); thread.Join(); Console.WriteLine("打完遊戲了嘛?" + (!thread.IsAlive ? "true" : "false")); Console.WriteLine($"{DateTime.Now}:走,逛街去"); Console.ReadKey(); } public static void OneTest() { Console.WriteLine(Thread.CurrentThread.Name + "開始打遊戲"); for (int i = 0; i < 10; i++) { Console.WriteLine($"{DateTime.Now}:第幾局:" + i); Thread.Sleep(TimeSpan.FromSeconds(2)); // 休眠 2 秒 } Console.WriteLine(Thread.CurrentThread.Name + "打完了"); }
Join() 也可以實現簡單的執行緒同步,即一個執行緒等待另一個執行緒完成。
ThreadState
是一個列舉,記錄了執行緒的狀態,我們可以從中判斷執行緒的生命週期和健康情況。
其列舉如下:
列舉 | 值 | 說明 |
---|---|---|
Initialized | 0 | 此狀態指示執行緒已初始化但尚未啟動。 |
Ready | 1 | 此狀態指示執行緒因無可用的處理器而等待使用處理器。 執行緒準備在下一個可用的處理器上執行。 |
Running | 2 | 此狀態指示執行緒當前正在使用處理器。 |
Standby | 3 | 此狀態指示執行緒將要使用處理器。 一次只能有一個執行緒處於此狀態。 |
Terminated | 4 | 此狀態指示執行緒已完成執行並已退出。 |
Transition | 6 | 此狀態指示執行緒在可以執行前等待處理器之外的資源。 例如,它可能正在等待其執行堆疊從磁碟中分頁。 |
Unknown | 7 | 執行緒的狀態未知。 |
Wait | 5 | 此狀態指示執行緒尚未準備好使用處理器,因為它正在等待外圍操作完成或等待資源釋放。 當執行緒就緒後,將對其進行重排。 |
但是裡面有很多列舉型別是沒有用處的,我們可以使用一個這樣的方法來獲取更加有用的資訊:
public static ThreadState GetThreadState(ThreadState ts) { return ts & (ThreadState.Unstarted | ThreadState.WaitSleepJoin | ThreadState.Stopped); }
根據 2.2 中的範例,我們修改一下 Main 中的方法:
static void Main(string[] args) { Thread thread = new Thread(OneTest); thread.Name = "小弟弟"; Console.WriteLine($"{DateTime.Now}:大家在吃飯,吃完飯後要帶小弟弟逛街"); Console.WriteLine("吃完飯了"); Console.WriteLine($"{DateTime.Now}:小弟弟開始玩遊戲"); Console.WriteLine("弟弟在幹嘛?(執行緒狀態):" + Enum.GetName(typeof(ThreadState), GetThreadState(thread.ThreadState))); thread.Start(); Console.WriteLine("弟弟在幹嘛?(執行緒狀態):" + Enum.GetName(typeof(ThreadState), GetThreadState(thread.ThreadState))); // 化妝 5 s Console.WriteLine("不管他,大姐姐化妝先"); Thread.Sleep(TimeSpan.FromSeconds(5)); Console.WriteLine("弟弟在幹嘛?(執行緒狀態):" + Enum.GetName(typeof(ThreadState), GetThreadState(thread.ThreadState))); Console.WriteLine($"{DateTime.Now}:化完妝,等小弟弟打完遊戲"); thread.Join(); Console.WriteLine("弟弟在幹嘛?(執行緒狀態):" + Enum.GetName(typeof(ThreadState), GetThreadState(thread.ThreadState))); Console.WriteLine("打完遊戲了嘛?" + (!thread.IsAlive ? "true" : "false")); Console.WriteLine($"{DateTime.Now}:走,逛街去"); Console.ReadKey(); }
程式碼看著比較亂,請複製到專案中執行一下。
輸出範例:
2020/4/11 11:01:48:大家在吃飯,吃完飯後要帶小弟弟逛街 吃完飯了 2020/4/11 11:01:48:小弟弟開始玩遊戲 弟弟在幹嘛?(執行緒狀態):Unstarted 弟弟在幹嘛?(執行緒狀態):Running 不管他,大姐姐化妝先 小弟弟開始打遊戲 2020/4/11 11:01:48:第幾局:0 2020/4/11 11:01:50:第幾局:1 2020/4/11 11:01:52:第幾局:2 弟弟在幹嘛?(執行緒狀態):WaitSleepJoin 2020/4/11 11:01:53:化完妝,等小弟弟打完遊戲 2020/4/11 11:01:54:第幾局:3 2020/4/11 11:01:56:第幾局:4 2020/4/11 11:01:58:第幾局:5 2020/4/11 11:02:00:第幾局:6 2020/4/11 11:02:02:第幾局:7 2020/4/11 11:02:04:第幾局:8 2020/4/11 11:02:06:第幾局:9 小弟弟打完了 弟弟在幹嘛?(執行緒狀態):Stopped 打完遊戲了嘛?true 2020/4/11 11:02:08:走,逛街去
可以看到 Unstarted
、WaitSleepJoin
、Running
、Stopped
四種狀態,即未開始(就緒)、阻塞、執行中、死亡。
.Abort()
方法不能在 .NET Core 上使用,不然會出現 System.PlatformNotSupportedException:“Thread abort is not supported on this platform.”
。
後面關於非同步的文章會講解如何實現終止。
由於 .NET Core 不支援,就不理會這兩個方法了。這裡只列出 API,不做範例。
方法 | 說明 |
---|---|
Abort() | 在呼叫此方法的執行緒上引發 ThreadAbortException,以開始終止此執行緒的過程。 呼叫此方法通常會終止執行緒。 |
Abort(Object) | 引發在其上呼叫的執行緒中的 ThreadAbortException以開始處理終止執行緒,同時提供有關執行緒終止的異常資訊。 呼叫此方法通常會終止執行緒。 |
Abort()
方法給執行緒注入 ThreadAbortException
異常,導致程式被終止。但是不一定可以終止執行緒。
執行緒的不確定性是指幾個並行執行的執行緒,不確定在下一刻 CPU 時間片會分配給誰(當然,分配有優先順序)。
對我們來說,多執行緒是同時執行
的,但一般 CPU 沒有那麼多核,不可能在同一時刻執行所有的執行緒。CPU 會決定某個時刻將時間片分配給多個執行緒中的一個執行緒,這就出現了 CPU 的時間片分配排程。
執行下面的程式碼範例,你可以看到,兩個執行緒列印的順序是不確定的,而且每次執行結果都不同。
CPU 有一套公式確定下一次時間片分配給誰,但是比較複雜,需要學習計算機組成原理和作業系統。
留著下次寫文章再講。
static void Main(string[] args) { Thread thread1 = new Thread(Test1); Thread thread2 = new Thread(Test2); thread1.Start(); thread2.Start(); Console.ReadKey(); } public static void Test1() { for (int i = 0; i < 10; i++) { Console.WriteLine("Test1:" + i); } } public static void Test2() { for (int i = 0; i < 10; i++) { Console.WriteLine("Test2:" + i); } }
Thread.Priority
屬性用於設定執行緒的優先順序,Priority
是一個 ThreadPriority 列舉,其列舉型別如下
列舉 | 值 | 說明 |
---|---|---|
AboveNormal | 3 | 可以將 安排在具有 Highest 優先順序的執行緒之後,在具有 Normal 優先順序的執行緒之前。 |
BelowNormal | 1 | 可以將 Thread 安排在具有 Normal 優先順序的執行緒之後,在具有 Lowest 優先順序的執行緒之前。 |
Highest | 4 | 可以將 Thread 安排在具有任何其他優先順序的執行緒之前。 |
Lowest | 0 | 可以將 Thread 安排在具有任何其他優先順序的執行緒之後。 |
Normal | 2 | 可以將 Thread 安排在具有 AboveNormal 優先順序的執行緒之後,在具有 BelowNormal 優先順序的執行緒之前。 預設情況下,執行緒具有 Normal 優先順序。 |
優先順序排序:Highest
> AboveNormal
> Normal
> BelowNormal
> Lowest
。
Thread.IsBackgroundThread
可以設定執行緒是否為後臺執行緒。
前臺執行緒的優先順序大於後臺執行緒,並且程式需要等待所有前臺執行緒執行完畢後才能關閉;而當程式關閉是,無論後臺執行緒是否在執行,都會強制退出。
當執行緒處於進入休眠狀態或解除休眠狀態時,會發生上下文切換,這就帶來了昂貴的消耗。
而執行緒不斷執行,就會消耗 CPU 時間,佔用 CPU 資源。
對於過短的等待,應該使用自旋(spin)方法,避免發生上下文切換;過長的等待應該使執行緒休眠,避免佔用大量 CPU 時間。
我們可以使用最為熟知的 Sleep()
方法休眠執行緒。有很多同步執行緒的型別,也使用了休眠手段等待執行緒(已經寫好草稿啦)。
自旋的意思是,沒事找事做。
例如:
public static void Test(int n) { int num = 0; for (int i=0;i<n;i++) { num += 1; } }
通過做一些簡單的運算,來消耗時間,從而達到等待的目的。
C# 中有關於自旋的自旋鎖和 Thread.SpinWait();
方法,在後面的執行緒同步分類中會說到自旋鎖。
Thread.SpinWait()
在極少數情況下,避免執行緒使用上下文切換很有用。其定義如下
public static void SpinWait(int iterations);
SpinWait 實質上是(處理器)使用了非常緊密的迴圈,並使用 iterations
引數指定的迴圈計數。 SpinWait 等待時間取決於處理器的速度。
SpinWait 無法使你準確控制等待時間,主要是使用一些鎖時用到,例如 Monitor.Enter。
到此這篇關於C#多執行緒系列之執行緒的建立和生命週期的文章就介紹到這了。希望對大家的學習有所幫助,也希望大家多多支援it145.com。
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45