在學習多線程以前,先來了解下幾個與多線程相關的概念。web
進程:進程是計算機的概念,程序在服務器運行時佔據所有計算資源的總和,一個應用程序運行起來就是一個進程,打開windows的任務管理器,以下圖數據庫
線程:線程也是計算機的概念,線程是進程的最小單位,也是程序在響應操做系統時的最小單位,一個進程至少由一個線程(主線程)構成。線程和進程同樣也會佔據必定的CPU、內存、網絡、硬盤IO等。一個線程隸屬於某個進程,進程銷燬,線程也隨之銷燬。windows
句柄:是一個long類型的數字,是操做系統用來標識應用程序的,有點主鍵或者身份證號碼的意思。服務器
多線程:一個進程或者說一個應用程序有多個線程在運行參與計算。網絡
Thread類是C#語言對線程對象的封裝。在.netframework1.0開始出現。在後面的多線程系列文章中會講到在不一樣的.netframework版本中多線程的API使用,在本篇文章中,先來初步認識多線程。多線程
1:CPU的多核技術和模擬核技術:併發
如計算機的參數概念4核8線程,所謂的4核8線程,4核指的是物理核心。經過超線程技術,用一個物理核模擬兩個虛擬核,每一個核兩個線程,總數爲8線程。四核八線程採用的超線程技術,是指每一個CPU核心沒有滿負荷運載時,其剩餘用量能夠模擬成虛擬的核心。單個物理核同一時間點只能處理一個線程,經過超線程技術能夠實現單個物理覈實現線程級別的並行計算。異步
2:CPU分片:實際上CPU在同一時刻只能處理一個任務,可是由於CPU的計算能力強大,在1秒內能夠響應不一樣的任務,把1秒的處理能力分紅10份,1到100毫秒處理任務A,101到200毫秒處理任務B,201到300毫秒處理任務C…,從宏觀角度來看,感受多個任務在併發執行,這個就是CPU的分片。性能
同步方法:發起調用,完成後才繼續下一行;很是符合開發思惟,有序由上至下執行;學習
異步方法:發起調用,不用等待完成,直接進入下一行,啓用一個新的線程來完成計算。
同步方法就像真誠的請人吃飯,客人說他有事,須要忙一下子,請吃飯的人等待客人忙完了再一塊兒吃飯。異步方法就像客氣的請人吃飯,客人說他有事,請吃飯的人說那你先去忙吧,而後本身就去吃飯了。
同步方法卡界面,主線程(UI)線程忙於計算,無暇他顧,異步方法不卡界面:主線程閒置,計算任務交給子線程完成,改善用戶體驗。如在winform中點擊按鈕採用同步的方式調用一個複雜的任務計算會致使界面短暫卡死,直到任務計算結束才能夠操做界面。
在web應用中發個短信通知,記錄一個日誌,均可以採用異步的方式去執行,客戶端不用等到短信發送成功或者日誌記錄成功才能接受到服務端的響應。
爲了可以清楚的說明狀況,這裏採用測試程序對比的方式,測試程序界面以下:
計算任務:
private void DoSomeThing(string btnName) { Console.WriteLine($"{btnName} 開始,當前線程id:{Thread.CurrentThread.ManagedThreadId}"); long lResult = 0; for (long i = 0; i < 1_000_000_000; i++) { lResult += i; } Console.WriteLine($"{btnName} 結束,當前線程id:{Thread.CurrentThread.ManagedThreadId}"); }
同步方式調用:
private void BtnSync_Click(object sender, EventArgs e) { Console.WriteLine($"btnSync_Click Start {Thread.CurrentThread.ManagedThreadId.ToString("00")}" + $" {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}"); Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); for (int i = 0; i < 5; i++) { string name = string.Format($"btnSync_Click_{i}"); this.DoSomeThing(name); } stopwatch.Stop(); Console.WriteLine($"同步方法耗時:{stopwatch.ElapsedMilliseconds}"); Console.WriteLine($"DoSomeThing End {Thread.CurrentThread.ManagedThreadId.ToString("00")} " + $"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}"); }
同步方式調用執行結果:
同步方式調用時CPU的使用狀況:
異步方式調用:
private void BtnAsync_Click(object sender, EventArgs e) { Console.WriteLine($"btnAsync_Click Start {Thread.CurrentThread.ManagedThreadId.ToString("00")}" + $" {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}"); Stopwatch stopwatch = new Stopwatch(); List<Task> tasks = new List<Task>(); stopwatch.Start(); for (int i = 0; i < 5; i++) { int k = i; tasks.Add(Task.Run(()=> { string name = string.Format($"btnAsync_Click{k}"); this.DoSomeThing(name); })); } Task.Run(()=> { Task.WaitAll(tasks.ToArray()); stopwatch.Stop(); Console.WriteLine($"異步方法耗時:{stopwatch.ElapsedMilliseconds}"); Console.WriteLine($"DoSomeThing End {Thread.CurrentThread.ManagedThreadId.ToString("00")}" + $" {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}"); }); }
異步方式調用執行結果:
異步方式調用時CPU的使用狀況:
同步方法慢,上圖耗時(16402毫秒),由於只有一個線程計算。異步方法快,上圖耗時(10524毫秒),由於有多個線程參與計算。觀察同步和異步調用時的使用狀況折線圖分析得知:多線程其實就是資源換取性能。在一個應用程序中是否是開啓的線程越多越好?不是的,由於開啓更多的資源,須要消耗更多的計算機資源,資源是有限的、資源調度也須要消耗資源,就像項目經理管理開發人員保證項目進度,項目經理調度(管理)開發人員也是須要工資的。
一個訂單表統計很耗時間,能用多線程優化性能麼?不能!這操做只包含一個任務,沒辦法並行計算,就像一個老師不能同時在兩個班級講課。若是一個操做在查詢數據庫的同時,須要調用接口、讀寫硬盤文件、作數據計算,這個能夠用多線程優化性能,由於多個任務能夠並行計算。
同步方法有序進行,異步多線程無序
啓動無序:線程資源是屬於非託管資源,是程序向操做系統申請的,由操做系統的調度策略決定,因此啓動順序是隨機的,cpu使用同一個線程計算同一個任務,執行時間也是不肯定的,so,結束也是無序的。在使用多線程的時候必定要當心,尤爲是多線程間有順序要求的時候經過延遲一點時間(Thread.Sleep())來控制執行順序,這是不靠譜的。