進程
計算機概念,程序在服務器運行時占據全部計算機資源總和,虛擬的。包含CPU、內存、網絡、硬盤
MSDN:
當一個程序開始運行時,它就是一個進程,進程包括運行中的程序和程序所使用到的內存和系統資源。
而一個進程又是由多個線程所組成的。
線程
計算機概念,進程在響應操作時最小單位,也包含CPU、內存、網絡、硬盤
MSDN:
線程是程序中的一個執行流,每個線程都有自己的專有寄存器(棧指針、程序計數器等),但代碼區是共享的,即不同的線程可以執行同樣的函數。
多線程
計算機概念,一個進程有多個線程同時運行。
MSDN:
多線程是指程序中包含多個執行流,即在一個程序中可以同時運行多個不同的線程來執行不同的任務,也就是說允許單個程序創建多個並行執行的線程來完成各自的任務。
為什么可以多線程
多個CPU的核可以並行工作,
4核8線程,這里的線程指的是模擬核
CPU分片
1s的處理能力分成1000份,操作系統調度着去響應不同的任務
從宏觀角度來說,就是多個任務在並發執行
從微觀角度來說,一個物理cpu同一時刻只能為一個任務服務
並行:多核之間叫並行
並發:CPU分片的並發
多線程其實是資源換性能,1 資源不是無限的 2 資源調度損耗
多線程的好處:
可以提高CPU的利用率。在多線程程序中,一個線程必須等待的時候,CPU可以 運行其它的線程而不是等待,這樣就大大提高了程序的效率。
多線程的不利之處:
線程也是程序,所以線程需要占用內存,線程越多占用內存也越多; 多線程需要協調和管理,所以需要CPU時間跟蹤線程;
線程之間對共享資源的訪問會相互影響,必須解決競用共享資源的問題;線程太多會導致控制太復雜,最終可能造成很多Bug;
同步與異步
同步:
發起調用,完成后才繼續下一行;非常符合開發思維,有序執行;
異步:
發起調用,不等待完成,直接進入下一行,啟動一個新線程來完成方法的計算
同步方法有序進行,異步多線程無序
啟動無序:線程資源是向操作系統申請的,由操作系統的調度策略決定,所以啟動順序隨機。同一個任務同一個線程,執行時間也不確定,CPU分片
以上相加,結束也無序
同步方法慢,異步多線程方法快
同步:只有一個線程計算 異步:因為多個線程並發計算
同步方法卡界面,異步多線程方法不卡界面
同步方法: 主線程(UI線程)忙於計算,無暇他顧
異步多線程方法: 異步多線程方法不卡界面:主線程閑置,計算任務交給子線程完成
同步代碼示例:
/// <summary> /// 同步方法 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void button1_Click(object sender, EventArgs e) { Console.WriteLine($"button1_Click*****Start{Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString()}"); for (int i = 0; i < 5; i++) { string name = string.Format($"button1_Click{i}"); this.DoSomethingLong(name); } Console.WriteLine($"button1_Click*****End{Thread.CurrentThread.ManagedThreadId} {DateTime.Now.ToString()}"); }
程序執行完成,我們可以清晰的看到,同步方法是有序的執行
委托異步多線程代碼示例:
/// <summary> /// 異步多線程 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void button2_Click_1(object sender, EventArgs e) { Console.WriteLine($"****************button2_Click Start " + $"{Thread.CurrentThread.ManagedThreadId.ToString("00")} " + $"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************"); Action<string> action = this.DoSomethingLong; for (int i = 0; i < 5; i++) { string name = string.Format($"button2_Click_1{i}"); action.BeginInvoke("button2_Click", null, null);//委托異步多線程調用 } Console.WriteLine($"****************button2_Click End " + $"{Thread.CurrentThread.ManagedThreadId.ToString("00")} " + $"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************"); }
程序運行之后,我們會發現運行的結果是無序的
委托異步多線程 回調
將后續動作通過回調參數傳遞進去,子線程完成計算后,去調用這個回調委托。
代碼示例:
/// <summary> /// 異步多線程回調、等待 /// Action /// Func /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void button3_Click(object sender, EventArgs e) { #region 回調 { Action<string> action = this.DoSomethingLong; IAsyncResult asyncResult = null;//是對異步調用操作的描述 AsyncCallback callback = mm => {//這里是一個委托 Console.WriteLine($"{object.ReferenceEquals(mm, asyncResult)}"); Console.WriteLine($"button3_Click計算成功了。{mm.AsyncState}。{Thread.CurrentThread.ManagedThreadId.ToString("00")}"); }; //回調 action.BeginInvoke("button3_Click", callback, "嘿嘿");//先執行action然后回調 callback委托 } #endregion }
委托異步多線程等待的三種方式:
IsComplate等待
通過IsComplate等待,主線程在等待,邊等待邊提示
#region IsComplate等待 { Action<string> action = this.DoSomethingLong; IAsyncResult asyncResult = null;//是對異步調用操作的描述 AsyncCallback callback = mm => { Console.WriteLine($"{object.ReferenceEquals(mm, asyncResult)}"); Console.WriteLine($"button3_Click計算成功了。{mm.AsyncState}。{Thread.CurrentThread.ManagedThreadId.ToString("00")}"); }; //回調 asyncResult= action.BeginInvoke("button3_Click", callback, "嘿嘿");//先執行action然后回調 callback委托 int i = 0; while (!asyncResult.IsCompleted) { Thread.Sleep(200); if (i < 2) { Console.WriteLine($"正在充電{++i * 10}%...."); } else{ Console.WriteLine($"充電完成完成99.999999%...."); } } Console.WriteLine("充電已完成,盡情的使用手機吧!"); } #endregion
程序執行完成的結果
注意:根據調用,首先執行DoSomethingLong 方法,因為DoSomethingLong方法里有密集操作,所以程序會邊等待邊執行while里的判斷語句以及方法體
此時DoSomethingLong里的密集操作已執行完畢,接着執行callback里的方法
WaitOne等待
WaitOne等待,即時等待 限時等待
asyncResult.AsyncWaitHandle.WaitOne();//直接等待任務完成
asyncResult.AsyncWaitHandle.WaitOne(-1);//一直等待任務完成
asyncResult.AsyncWaitHandle.WaitOne(1000);//最多等待1000ms,超時就不等了
EndInvoke等待
即時等待,而且可以獲取委托的返回值 一個異步操作只能End一次
#region EndInvoke Func<int> func = ()=> { Thread.Sleep(2000);
return DateTime.Now.Hour; }; int Result = func.Invoke(); IAsyncResult asyncResult = func.BeginInvoke(ar=> { //在方法里拿到結果 // int IResult = func.EndInvoke(ar); //對於每個異步操作,只能調用一次EndInvoke }, null); //在方法外得到結果 int EndResult = func.EndInvoke(asyncResult); #endregion