網(wǎng)站首頁(yè) 編程語(yǔ)言 正文
1,獲取當(dāng)前線程信息
Thread.CurrentThread
?是一個(gè) 靜態(tài)的 Thread 類,Thread 的CurrentThread
?屬性,可以獲取到當(dāng)前運(yùn)行線程的一些信息,其定義如下:
public static System.Threading.Thread CurrentThread { get; }
Thread 類有很多屬性和方法,這里就不列舉了,后面的學(xué)習(xí)會(huì)慢慢熟悉更多 API 和深入了解使用。
這里有一個(gè)簡(jiǎn)單的示例:
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("線程標(biāo)識(shí):" + thisTHread.Name); Console.WriteLine("當(dāng)前地域:" + thisTHread.CurrentCulture.Name); // 當(dāng)前地域 Console.WriteLine("線程執(zhí)行狀態(tài):" + thisTHread.IsAlive); Console.WriteLine("是否為后臺(tái)線程:" + thisTHread.IsBackground); Console.WriteLine("是否為線程池線程"+thisTHread.IsThreadPoolThread); }
輸出
線程標(biāo)識(shí):Test 當(dāng)前地域:zh-CN 線程執(zhí)行狀態(tài):True 是否為后臺(tái)線程:False 是否為線程池線程False
2,管理線程狀態(tài)
一般認(rèn)為,線程有五種狀態(tài):
新建(new 對(duì)象) 、就緒(等待CPU調(diào)度)、運(yùn)行(CPU正在運(yùn)行)、阻塞(等待阻塞、同步阻塞等)、死亡(對(duì)象釋放)。
理論的東西不說(shuō)太多,直接擼代碼。
2.1 啟動(dòng)與參數(shù)傳遞
新建線程簡(jiǎn)直滾瓜爛熟,無(wú)非?new
?一下,然后?Start()
。
Thread thread = new Thread();
Thread 的構(gòu)造函數(shù)有四個(gè):
public Thread(ParameterizedThreadStart start); public Thread(ThreadStart start); public Thread(ParameterizedThreadStart start, int maxStackSize); public Thread(ThreadStart start, int maxStackSize);
我們以啟動(dòng)新的線程時(shí)傳遞參數(shù)來(lái)舉例,使用這四個(gè)構(gòu)造函數(shù)呢?
2.1.1 ParameterizedThreadStart
ParameterizedThreadStart 是一個(gè)委托,構(gòu)造函數(shù)傳遞的參數(shù)為需要執(zhí)行的方法,然后在?Start
?方法中傳遞參數(shù)。
需要注意的是,傳遞的參數(shù)類型為 object,而且只能傳遞一個(gè)。
代碼示例如下:
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("新的線程已經(jīng)啟動(dòng)"); Console.WriteLine(str); }
2.1.2 使用靜態(tài)變量或類成員變量
此種方法不需要作為參數(shù)傳遞,各個(gè)線程共享堆棧。
優(yōu)點(diǎn)是不需要裝箱拆箱,多線程可以共享空間;缺點(diǎn)是變量是大家都可以訪問(wèn),此種方式在多線程競(jìng)價(jià)時(shí),可能會(huì)導(dǎo)致多種問(wèn)題(可以加鎖解決)。
下面使用兩個(gè)變量實(shí)現(xiàn)數(shù)據(jù)傳遞:
class Program { private string A = "成員變量"; public static string B = "靜態(tài)變量"; static void Main(string[] args) { // 創(chuàng)建一個(gè)類 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("新的線程已經(jīng)啟動(dòng)"); Console.WriteLine(A); // 本身對(duì)象的其它成員 } public static void OneTest2() { Console.WriteLine("新的線程已經(jīng)啟動(dòng)"); Console.WriteLine(B); // 全局靜態(tài)變量 } }
2.1.3 委托與Lambda
原理是 Thread 的構(gòu)造函數(shù)?public Thread(ThreadStart start);
,ThreadStart?
是一個(gè)委托,其定義如下
public delegate void ThreadStart();
使用委托的話,可以這樣寫(xiě)
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("新的線程已經(jīng)啟動(dòng)"); }
有那么一點(diǎn)點(diǎn)麻煩,不過(guò)我們可以使用 Lambda 快速實(shí)現(xiàn)。
使用 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("新的線程已經(jīng)啟動(dòng)"); }
提示:如果需要處理的算法比較簡(jiǎn)單的話,可以直接寫(xiě)進(jìn)委托中,不需要另外寫(xiě)方法啦。
可以看到,C# 是多么的方便。
2.2 暫停與阻塞
Thread.Sleep()
?方法可以將當(dāng)前線程掛起一段時(shí)間,Thread.Join()
?方法可以阻塞當(dāng)前線程一直等待另一個(gè)線程運(yùn)行至結(jié)束。
在等待線程?Sleep()
?或?Join()
?的過(guò)程中,線程是阻塞的(Blocket)。
? ? 阻塞的定義:當(dāng)線程由于特點(diǎn)原因暫停執(zhí)行,那么它就是阻塞的。?
? ? 如果線程處于阻塞狀態(tài),線程就會(huì)交出他的 CPU 時(shí)間片,并且不會(huì)消耗 CPU 時(shí)間,直至阻塞結(jié)束。?
? ? 阻塞會(huì)發(fā)生上下文切換。
代碼示例如下:
static void Main(string[] args) { Thread thread = new Thread(OneTest); thread.Name = "小弟弟"; Console.WriteLine($"{DateTime.Now}:大家在吃飯,吃完飯后要帶小弟弟逛街"); Console.WriteLine("吃完飯了"); Console.WriteLine($"{DateTime.Now}:小弟弟開(kāi)始玩游戲"); 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 + "開(kāi)始打游戲"); for (int i = 0; i < 10; i++) { Console.WriteLine($"{DateTime.Now}:第幾局:" + i); Thread.Sleep(TimeSpan.FromSeconds(2)); // 休眠 2 秒 } Console.WriteLine(Thread.CurrentThread.Name + "打完了"); }
Join() 也可以實(shí)現(xiàn)簡(jiǎn)單的線程同步,即一個(gè)線程等待另一個(gè)線程完成。
2.3 線程狀態(tài)
ThreadState
?是一個(gè)枚舉,記錄了線程的狀態(tài),我們可以從中判斷線程的生命周期和健康情況。
其枚舉如下:
枚舉 | 值 | 說(shuō)明 |
---|---|---|
Initialized | 0 | 此狀態(tài)指示線程已初始化但尚未啟動(dòng)。 |
Ready | 1 | 此狀態(tài)指示線程因無(wú)可用的處理器而等待使用處理器。 線程準(zhǔn)備在下一個(gè)可用的處理器上運(yùn)行。 |
Running | 2 | 此狀態(tài)指示線程當(dāng)前正在使用處理器。 |
Standby | 3 | 此狀態(tài)指示線程將要使用處理器。 一次只能有一個(gè)線程處于此狀態(tài)。 |
Terminated | 4 | 此狀態(tài)指示線程已完成執(zhí)行并已退出。 |
Transition | 6 | 此狀態(tài)指示線程在可以執(zhí)行前等待處理器之外的資源。 例如,它可能正在等待其執(zhí)行堆棧從磁盤中分頁(yè)。 |
Unknown | 7 | 線程的狀態(tài)未知。 |
Wait | 5 | 此狀態(tài)指示線程尚未準(zhǔn)備好使用處理器,因?yàn)樗诘却鈬僮魍瓿苫虻却Y源釋放。 當(dāng)線程就緒后,將對(duì)其進(jìn)行重排。 |
但是里面有很多枚舉類型是沒(méi)有用處的,我們可以使用一個(gè)這樣的方法來(lái)獲取更加有用的信息:
public static ThreadState GetThreadState(ThreadState ts) { return ts & (ThreadState.Unstarted | ThreadState.WaitSleepJoin | ThreadState.Stopped); }
根據(jù)?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}:小弟弟開(kāi)始玩游戲"); Console.WriteLine("弟弟在干嘛?(線程狀態(tài)):" + Enum.GetName(typeof(ThreadState), GetThreadState(thread.ThreadState))); thread.Start(); Console.WriteLine("弟弟在干嘛?(線程狀態(tài)):" + Enum.GetName(typeof(ThreadState), GetThreadState(thread.ThreadState))); // 化妝 5 s Console.WriteLine("不管他,大姐姐化妝先"); Thread.Sleep(TimeSpan.FromSeconds(5)); Console.WriteLine("弟弟在干嘛?(線程狀態(tài)):" + Enum.GetName(typeof(ThreadState), GetThreadState(thread.ThreadState))); Console.WriteLine($"{DateTime.Now}:化完妝,等小弟弟打完游戲"); thread.Join(); Console.WriteLine("弟弟在干嘛?(線程狀態(tài)):" + Enum.GetName(typeof(ThreadState), GetThreadState(thread.ThreadState))); Console.WriteLine("打完游戲了嘛?" + (!thread.IsAlive ? "true" : "false")); Console.WriteLine($"{DateTime.Now}:走,逛街去"); Console.ReadKey(); }
代碼看著比較亂,請(qǐng)復(fù)制到項(xiàng)目中運(yùn)行一下。
輸出示例:
2020/4/11 11:01:48:大家在吃飯,吃完飯后要帶小弟弟逛街 吃完飯了 2020/4/11 11:01:48:小弟弟開(kāi)始玩游戲 弟弟在干嘛?(線程狀態(tài)):Unstarted 弟弟在干嘛?(線程狀態(tài)):Running 不管他,大姐姐化妝先 小弟弟開(kāi)始打游戲 2020/4/11 11:01:48:第幾局:0 2020/4/11 11:01:50:第幾局:1 2020/4/11 11:01:52:第幾局:2 弟弟在干嘛?(線程狀態(tài)):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 小弟弟打完了 弟弟在干嘛?(線程狀態(tài)):Stopped 打完游戲了嘛?true 2020/4/11 11:02:08:走,逛街去
可以看到?Unstarted
、WaitSleepJoin
、Running
、Stopped
四種狀態(tài),即未開(kāi)始(就緒)、阻塞、運(yùn)行中、死亡。
2.4 終止
.Abort()
?方法不能在 .NET Core 上使用,不然會(huì)出現(xiàn)?System.PlatformNotSupportedException:“Thread abort is not supported on this platform.”
?。
后面關(guān)于異步的文章會(huì)講解如何實(shí)現(xiàn)終止。
由于 .NET Core 不支持,就不理會(huì)這兩個(gè)方法了。這里只列出 API,不做示例。
方法 | 說(shuō)明 |
---|---|
Abort() | 在調(diào)用此方法的線程上引發(fā) ThreadAbortException,以開(kāi)始終止此線程的過(guò)程。 調(diào)用此方法通常會(huì)終止線程。 |
Abort(Object) | 引發(fā)在其上調(diào)用的線程中的 ThreadAbortException以開(kāi)始處理終止線程,同時(shí)提供有關(guān)線程終止的異常信息。 調(diào)用此方法通常會(huì)終止線程。 |
Abort()
?方法給線程注入?ThreadAbortException
?異常,導(dǎo)致程序被終止。但是不一定可以終止線程。
2.5 線程的不確定性
線程的不確定性是指幾個(gè)并行運(yùn)行的線程,不確定在下一刻 CPU 時(shí)間片會(huì)分配給誰(shuí)(當(dāng)然,分配有優(yōu)先級(jí))。
對(duì)我們來(lái)說(shuō),多線程是同時(shí)運(yùn)行
的,但一般 CPU 沒(méi)有那么多核,不可能在同一時(shí)刻執(zhí)行所有的線程。CPU 會(huì)決定某個(gè)時(shí)刻將時(shí)間片分配給多個(gè)線程中的一個(gè)線程,這就出現(xiàn)了 CPU 的時(shí)間片分配調(diào)度。
執(zhí)行下面的代碼示例,你可以看到,兩個(gè)線程打印的順序是不確定的,而且每次運(yùn)行結(jié)果都不同。
CPU 有一套公式確定下一次時(shí)間片分配給誰(shuí),但是比較復(fù)雜,需要學(xué)習(xí)計(jì)算機(jī)組成原理和操作系統(tǒng)。
留著下次寫(xiě)文章再講。
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); } }
2.6 線程優(yōu)先級(jí)、前臺(tái)線程和后臺(tái)線程
Thread.Priority
?屬性用于設(shè)置線程的優(yōu)先級(jí),Priority
?是一個(gè) ThreadPriority 枚舉,其枚舉類型如下
枚舉 | 值 | 說(shuō)明 |
---|---|---|
AboveNormal | 3 | 可以將 安排在具有?Highest ?優(yōu)先級(jí)的線程之后,在具有?Normal ?優(yōu)先級(jí)的線程之前。 |
BelowNormal | 1 | 可以將 Thread 安排在具有?Normal ?優(yōu)先級(jí)的線程之后,在具有?Lowest ?優(yōu)先級(jí)的線程之前。 |
Highest | 4 | 可以將 Thread 安排在具有任何其他優(yōu)先級(jí)的線程之前。 |
Lowest | 0 | 可以將 Thread 安排在具有任何其他優(yōu)先級(jí)的線程之后。 |
Normal | 2 | 可以將 Thread 安排在具有?AboveNormal ?優(yōu)先級(jí)的線程之后,在具有?BelowNormal ?優(yōu)先級(jí)的線程之前。 默認(rèn)情況下,線程具有?Normal ?優(yōu)先級(jí)。 |
優(yōu)先級(jí)排序:Highest
?>?AboveNormal
?>?Normal
?>?BelowNormal
?>?Lowest
。
Thread.IsBackgroundThread
?可以設(shè)置線程是否為后臺(tái)線程。
前臺(tái)線程的優(yōu)先級(jí)大于后臺(tái)線程,并且程序需要等待所有前臺(tái)線程執(zhí)行完畢后才能關(guān)閉;而當(dāng)程序關(guān)閉是,無(wú)論后臺(tái)線程是否在執(zhí)行,都會(huì)強(qiáng)制退出。
2.7 自旋和休眠
當(dāng)線程處于進(jìn)入休眠狀態(tài)或解除休眠狀態(tài)時(shí),會(huì)發(fā)生上下文切換,這就帶來(lái)了昂貴的消耗。
而線程不斷運(yùn)行,就會(huì)消耗 CPU 時(shí)間,占用 CPU 資源。
對(duì)于過(guò)短的等待,應(yīng)該使用自旋(spin)方法,避免發(fā)生上下文切換;過(guò)長(zhǎng)的等待應(yīng)該使線程休眠,避免占用大量 CPU 時(shí)間。
我們可以使用最為熟知的?Sleep()
?方法休眠線程。有很多同步線程的類型,也使用了休眠手段等待線程(已經(jīng)寫(xiě)好草稿啦)。
自旋的意思是,沒(méi)事找事做。
例如:
public static void Test(int n) { int num = 0; for (int i=0;i<n;i++) { num += 1; } }
通過(guò)做一些簡(jiǎn)單的運(yùn)算,來(lái)消耗時(shí)間,從而達(dá)到等待的目的。
C# 中有關(guān)于自旋的自旋鎖和?Thread.SpinWait();
?方法,在后面的線程同步分類中會(huì)說(shuō)到自旋鎖。
Thread.SpinWait()
?在極少數(shù)情況下,避免線程使用上下文切換很有用。其定義如下
public static void SpinWait(int iterations);
SpinWait 實(shí)質(zhì)上是(處理器)使用了非常緊密的循環(huán),并使用?iterations
?參數(shù)指定的循環(huán)計(jì)數(shù)。 SpinWait 等待時(shí)間取決于處理器的速度。
SpinWait 無(wú)法使你準(zhǔn)確控制等待時(shí)間,主要是使用一些鎖時(shí)用到,例如 Monitor.Enter。
原文鏈接:https://www.cnblogs.com/whuanle/p/12708824.html
相關(guān)推薦
- 2022-03-20 關(guān)于Rancher部署并導(dǎo)入K8S集群的問(wèn)題_云其它
- 2023-04-06 ndarray的轉(zhuǎn)置(numpy.transpose()與A.T命令對(duì)比分析)_python
- 2023-07-08 qt修改默認(rèn)構(gòu)建路徑
- 2022-12-06 React?Redux應(yīng)用示例詳解_React
- 2022-07-26 arduino上傳程序出錯(cuò)不成功常見(jiàn)的問(wèn)題解決
- 2021-12-14 如何利用C語(yǔ)言輸出3D立體感心形圖詳解_C 語(yǔ)言
- 2022-10-02 react?redux及redux持久化示例詳解_React
- 2022-09-06 pandas?如何將字符串映射為數(shù)字_python
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細(xì)win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運(yùn)行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲(chǔ)小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運(yùn)算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認(rèn)證信息的處理
- Spring Security之認(rèn)證過(guò)濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯(cuò)誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實(shí)現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡(jiǎn)單動(dòng)態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對(duì)象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊(duì)列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠(yuǎn)程分支