C#?異步多線程入門(mén)基礎(chǔ)
下一篇:C# 異步多線程入門(mén)到精通之Thread篇
進(jìn)程、線程
1. 進(jìn)程
首先了解,什么是線程? 即一個(gè)應(yīng)用程序運(yùn)行時(shí),占用資源的綜合是一個(gè)進(jìn)程。Windows 任務(wù)管理器里面可以看到,里面一個(gè)個(gè)都是在運(yùn)行的進(jìn)程。
2. 線程
線程是執(zhí)行流的最小單位。線程其實(shí)是看不到的,其實(shí)也可以,例如 Windows 任務(wù)管理器:正在運(yùn)行 272 個(gè)進(jìn)程,272 個(gè)進(jìn)程運(yùn)行了 3909 個(gè)線程,也就是一個(gè)進(jìn)程可以擁有多個(gè)線程。
分時(shí)、分片
現(xiàn)在有個(gè)怪相,CPU 實(shí)在太快了,內(nèi)存顯卡其他硬件資源其實(shí)都跟不上 CPU 的速度,于是就產(chǎn)生了分片的概念。從微觀角度來(lái)講,以前電腦很多都是單核,一時(shí)刻只能執(zhí)行一個(gè)線程,按照這個(gè)道理,為什么我們的計(jì)算機(jī)還可以同時(shí)運(yùn)行許多個(gè)應(yīng)用呢。但從宏觀來(lái)說(shuō)是并發(fā)的,多個(gè)應(yīng)用同時(shí)執(zhí)行,我們既可以?huà)呃滓部梢酝曛┲爰埮七€可以聽(tīng)音樂(lè)。這就是分片,分片會(huì)產(chǎn)生一個(gè)上下文,假設(shè)當(dāng)前執(zhí)行掃雷線程,下一個(gè)時(shí)刻執(zhí)行蜘蛛紙牌線程,CPU 會(huì)將掃雷線程上下文保存起來(lái),切換成蜘蛛紙牌線程,這樣進(jìn)行來(lái)回調(diào)度,從宏觀來(lái)看是并發(fā)的。
這個(gè)補(bǔ)充一點(diǎn)額外知識(shí),多 CPU 多核,本身就可以完成多個(gè)線程的計(jì)算,可以獨(dú)立工作。4核8線程,核就是物理的核,線程是虛擬的核,每個(gè)核可以進(jìn)行分片做并發(fā)的。
同步、異步
我們開(kāi)發(fā)人員口中常說(shuō)的同步、異步,其實(shí)是對(duì)方法執(zhí)行的描述。因?yàn)榫幊陶Z(yǔ)言本身是沒(méi)有線程的,它只能去向操作系統(tǒng)申請(qǐng)線程,去執(zhí)行代碼。
同步方法,代碼執(zhí)行第一行到最后一行依次執(zhí)行到結(jié)束,完成第一行之后進(jìn)入下第二行直到最后一行,這就是同步,阻塞式的。
異步方法,不會(huì)等待當(dāng)前行執(zhí)行完成,就會(huì)進(jìn)行下一行執(zhí)行代碼,非阻塞式的。
異步、多線程
多線程,就是多個(gè)執(zhí)行流,同時(shí)執(zhí)行。在 C# 中多線程就是多個(gè)并發(fā)的 Thread 開(kāi)啟多個(gè)線程處理任務(wù),利用的可能是 CPU 的多核,也可能是單核 CPU 分片完成執(zhí)行的任務(wù)。
異步,其實(shí)是硬件式的異步,其實(shí)這個(gè)不太好理解。這里就拿文件寫(xiě)入來(lái)說(shuō)
多線程情況,線程會(huì)一直從文件寫(xiě)入開(kāi)始到結(jié)束,都參與工作這件事,也就是 CPU 會(huì)處理寫(xiě)文件操作,線程會(huì)一直等待 CPU 向磁盤(pán)寫(xiě)入文件直到完成。
異步情況,線程會(huì)給 CPU 發(fā)個(gè)指令與文件流交個(gè)操作系統(tǒng),線程就可以忙別的事情去了,也就是利用硬件的特性,發(fā)個(gè)指令讓操作系統(tǒng)完成文件寫(xiě)入,線程去執(zhí)行其他任務(wù),等 CPU 寫(xiě)完后發(fā)個(gè)指令回來(lái),通知到線程。
有同學(xué)會(huì)問(wèn)這個(gè)異步怎么寫(xiě),其實(shí)在程序中是寫(xiě)不了這個(gè)的,這是操作系統(tǒng)底層的東西,在 WPF 里面就可以直接調(diào)用。 C# 中常說(shuō)的異步多線程指的是 ThreadPool、Task ,都是基于 Thread 完成的,只是 C# 語(yǔ)言進(jìn)行了封裝的。
異步多線程效率
說(shuō)起異步很多人都知道,同步方法慢,異步多線程快,這個(gè)大家心里面都是這么認(rèn)為的。但效率究竟提升了多少呢
我們看下面程序,定義一個(gè)普通方法 Sum 做些 CPU 密集型運(yùn)算,然后在 Main 方法,分別同步與異步,執(zhí)行 Sum 方法。即同樣的任務(wù)使用單線程(主線程)運(yùn)行五次,再使用多線程開(kāi)啟 5 個(gè)線程,分別執(zhí)行。
public static void Sum(int f) { Console.WriteLine($"第{f}次 start:{DateTime.Now.ToLongTimeString()},Thread:{Thread.CurrentThread.ManagedThreadId}"); decimal s = 0; for (int i = 0; i < 1000000000; i++) { s = s + i; } Console.WriteLine($"第{f}次 end:{DateTime.Now.ToLongTimeString()},Thread:{Thread.CurrentThread.ManagedThreadId}"); } static void Main(string[] args) { Console.WriteLine($"同步 start:{DateTime.Now.ToLongTimeString()},Thread:{Thread.CurrentThread.ManagedThreadId}"); for (int i = 0; i < 5; i++) { Sum(i); } Console.WriteLine($"同步 end:{DateTime.Now.ToLongTimeString()},Thread:{Thread.CurrentThread.ManagedThreadId}"); Console.WriteLine(); Console.WriteLine($"異步 start:{DateTime.Now.ToLongTimeString()},Thread:{Thread.CurrentThread.ManagedThreadId}"); for (int i = 0; i < 5; i++) { Action<int> action = Sum; action.BeginInvoke(i,null,null); } Console.WriteLine($"異步 end:{DateTime.Now.ToLongTimeString()},Thread:{Thread.CurrentThread.ManagedThreadId}"); Console.ReadKey(); }
啟動(dòng)程序,可以看到,同步方法1個(gè)線程執(zhí)行了 2 分 40 秒,異步方法 5 個(gè)線程執(zhí)行了 52 秒,很顯然異步方法快與同步方法四倍多。
說(shuō)到這可能有的同學(xué)會(huì)發(fā)現(xiàn),同步1個(gè)線程,異步5個(gè)線程,為什么效率不提升五倍呢,不是線性增長(zhǎng)呢?其實(shí),異步效率提升受限于資源限制、上下文切換成本
上面我們說(shuō)過(guò) CPU 的分時(shí)分片,即使單核也可以同時(shí)運(yùn)行多個(gè)程序,那既要馬兒跑得快,又要馬兒不吃草,怎么可能呢,這就是上下文切換的管理成本。
資源限制,異步效率不高也可能資源不夠。如下,我們啟動(dòng)資源管理器并啟動(dòng)程序,可以看到同步時(shí) CPU 使用的資源并不高執(zhí)行但時(shí)間長(zhǎng),當(dāng)多線程時(shí) CPU 達(dá)到了 100% 但執(zhí)行時(shí)間短,也就是一種資源換時(shí)間策略。
多線程無(wú)序性
因?yàn)橛?jì)算機(jī)的分時(shí)分片,會(huì)使得多線程無(wú)序。即啟動(dòng)無(wú)序、執(zhí)行無(wú)序、結(jié)束無(wú)序。
啟動(dòng)無(wú)序,線程是操作系統(tǒng)的,C# 并沒(méi)有線程,C# 需要向操作系統(tǒng)申請(qǐng)線程,假設(shè)同時(shí)或者有序向向操作系統(tǒng)申請(qǐng)線程,但操作系統(tǒng)并不是按順序給線程,可能后申請(qǐng)的先拿到線程,也可能先申請(qǐng)的后拿到線程。
執(zhí)行無(wú)序,程序拿到了操作系統(tǒng)分片的線程后,我們執(zhí)行的任務(wù)運(yùn)氣好了可能后申請(qǐng)的先執(zhí)行,運(yùn)氣差了先申請(qǐng)的后執(zhí)行,都是非常常見(jiàn)的。
結(jié)束無(wú)序,這個(gè)受執(zhí)行無(wú)序、分時(shí)分片運(yùn)氣、任務(wù)量的影響。開(kāi)始執(zhí)行無(wú)序已經(jīng)說(shuō)過(guò);分時(shí)分片運(yùn)氣,當(dāng)我的 CPU 執(zhí)行一個(gè)任務(wù),并不是任務(wù)執(zhí)行完了才會(huì)進(jìn)行上下文的切換去執(zhí)行其他任務(wù),而是可以隨時(shí)暫停切換執(zhí)行其他任務(wù)的;任務(wù)量就是,做的任務(wù)多少不同,即使同一時(shí)刻開(kāi)始執(zhí)行,完成的時(shí)間必然不一樣。
擴(kuò)展
1 . 同一個(gè)線程做相同的事情,耗時(shí)時(shí)間一樣嗎?
答案:不一樣,應(yīng)為CPU是分片的,運(yùn)氣好瘋狂拿時(shí)間片,運(yùn)氣不好十次也拿不到。
異步多線程版本
在 .NET 中隨著時(shí)間的發(fā)展,線程是有許多個(gè)版本的 1.0 Thread、2.0 TthreadPool、3.0 Task、4.0 Parallel 等,每個(gè)版本都是其特點(diǎn),后面我們一一進(jìn)行講解。
以上就是C# 異步多線程入門(mén)基礎(chǔ)的詳細(xì)內(nèi)容,更多關(guān)于C# 異步多線程的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Winform學(xué)生信息管理系統(tǒng)登陸窗體設(shè)計(jì)(1)
這篇文章主要為大家詳細(xì)介紹了Winform學(xué)生信息管理系統(tǒng)登陸窗體設(shè)計(jì)思路,感興趣的小伙伴們可以參考一下2016-05-05c# 使用Json.NET實(shí)現(xiàn)json序列化
這篇文章主要介紹了詳解C#中的JSON序列化方法,幫助大家更好的理解和學(xué)習(xí)使用c#,感興趣的朋友可以了解下2021-05-05C#開(kāi)發(fā)答題贏錢(qián)游戲(自動(dòng)答題器)
現(xiàn)在最火的直播游戲,那就是答題贏錢(qián)直播了,如百萬(wàn)英雄、芝士超人、花椒直播、沖頂大會(huì)等等,這些游戲的玩法都很簡(jiǎn)單,答對(duì)12題即可瓜分獎(jiǎng)金了。玩法雖簡(jiǎn)單但是完全答對(duì)12題難度就挺高了,下面小編給大家?guī)?lái)了C#開(kāi)發(fā)答題贏錢(qián)游戲,需要的朋友參考下吧2018-01-01C#文件操作、讀取文件、Debug/Trace類(lèi)用法
這篇文章介紹了C#文件操作、讀取文件、Debug/Trace類(lèi)的用法,文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-03-03使用C#調(diào)用系統(tǒng)API實(shí)現(xiàn)內(nèi)存注入的代碼
使用C#調(diào)用系統(tǒng)API實(shí)現(xiàn)內(nèi)存注入的代碼,學(xué)習(xí)c#的朋友可以參考下。2011-06-06C#模擬MSN窗體抖動(dòng)的實(shí)現(xiàn)代碼
這篇文章主要介紹了C#模擬MSN窗體抖動(dòng)的實(shí)現(xiàn)代碼,非常實(shí)用的一個(gè)功能,需要的朋友可以參考下2014-08-08C#使用selenium實(shí)現(xiàn)操作瀏覽器并且截圖
這篇文章主要為大家詳細(xì)介紹了C#如何使用selenium組件實(shí)現(xiàn)操作瀏覽器并且截圖,文中的示例代碼簡(jiǎn)潔易懂,有需要的小伙伴可以參考一下2024-01-01