委托
如何聲明一個委托:通過 【delegate 返回值類型 委托名稱】 的格式來定義
如何使用一個委托:使用new關鍵字,并傳入和聲明委托的構造相同的方法名,比如:new 委托名稱(與委托的參數和返回值相同的一個方法名)
如何調用一個委托:將new關鍵字返回的實例調用Invoke方法,或者像調用一個方法一樣調用它,就像方法一個調用都可以
什么是多播委托:顧名思義,多播委托可以注冊多個實現了委托的方法。
如何使用多播委托:通過+=,-=注冊,取消注冊委托
預定義委托:通過Action,Func來實現,他們的區別在于Action沒有返回值,Func有返回值,返回值在最后一個泛型里。
匿名函數經常和委托一起使用,語法:()=>{}
事件
事件是在委托的基礎上做的一個安全的處理,避免直接調用委托,而是通過+=,-=這種方式去注冊,取消注冊委托。并且在內部實現了線程安全的操作。
如果要研究源碼可以在SharpLab中看到用原始方法是如何實現了高級語法的。
多線程
什么是線程?
一個進程有多個線程,線程是操作系統中獨立運行的最小單位。
為什么要用多線程?
希望能同時運行多個任務,提高效率。
什么是線程池?
是一組預先創建的線程,可以被重復利用來執行任務。
什么是線程安全?
多個線程同時訪問共享資源時,對它的訪問不會產生數據不一致的后果。
同步機制:用于協調多個線程訪問的執行順序去訪問共享資源,避免數據不一致的問題。通過lock(obj){}的方式實現同步。
lock語法糖實際是對Monitor的一個封裝。
原子操作:在執行任務時要么全部執行,要么全部不執行,確保了數據的一致性。 通過Interlocked類實現原子操作
并行操作:使用并行操作(PLinq)可以大幅提高效率,使用AsParallel方法可以確保并行操作的執行。
如果前臺線程消亡,后臺線程也要跟著消亡,Main函數就是一個前臺線程。
如何創建一個線程?
new Thread(方法名稱).Start(方法的參數)
{
IsBackground = ,
....... // 配置
}
Join方法可以讓當前線程先執行完畢,確保后面的線程不會在它之前執行。
Interrupt可以中斷線程的執行
信號量(Semaphore):可以理解為高速公路的閘口和車道數,第一個參數控制開啟的閘口數,第二個參數控制車道數。
異步編程
異步不意味著多線程,單線程也可以是異步的。異步默認是使用線程池的,可以從線程ID中觀察的到,每次的ID都不一樣。
多線程適合CPU密集型的操作,適合長期運行的任務
異步適合IO密集型的操作,適合短暫的小任務,例如:服務器的后端接口;異步可以查看到線程執行的狀態。
異步任務的創建:
1、Task.Run()
2、Task.Factory.StartNew()
3、new Task().Start();
特性:
1、將方法標記為async后,方可在方法中使用await,還有一種同步的寫法但不推薦:InvokeTask().GetAwaiter().GetResult();
2、async+await會將方法包裝為狀態機
3、異步編程具有傳染性
async void這種寫法幾乎只用于對事件的注冊。
常見的阻塞情形:
1、Task.Wait() Task.Result如果任務沒有完成,則會阻塞當前線程,容易造成死鎖。
2、InvokeTask().GetAwaiter().GetResult(); 依舊是個阻塞寫法
3、Thread.Sleep(),使用異步時應該為Task.Delay(),這種寫法會立即釋放當前線程
4、IO耗時的操作
如何開啟多個異步任務?
Task.WhenAll(task),Task.WhenAny(task),不要在for循環中await。
如何取消異步任務?
使用CancellationToken實例的Cancel方法,同時配合IsCancellationRequested屬性可以在任務中精確控制是否繼續執行任務。
任務超時如何實現?
在創建CancellationTokenSource時傳入超時時間,比如:new CancellationTokenSource(2000);
注意事項
需要注意的是每次需要手動釋放CancellationTokenSource
??運算符,當左邊的參數不為空則返回它,如果為空則返回右邊的參數
有哪些同步機制?
SemaphoreSlim 推薦,用法簡單
Channel
第三方庫 - AsyncLock,比較復雜適用于大型項目
如何在在同步方法中調用異步?
1、應避免使用xxx.Wait(),xxx.Result,有可能導致死鎖,且無法捕獲異常。
2、應避免使用async void,無法捕獲到內部異常,內部不加異常捕獲會導致程序掛掉
3、InvokeTask().GetAwaiter().GetResult(); 可以捕獲到內部異常。
4、SafeFireForget:async void SafeFireForget(Task task, Action? completed, Action<Exception> expection),第一個參數是異步執行的業務方法,第二個參數是異步任務完成后的回調委托,第三個參數是異步任務出現異常的回調委托
5、ContinueWith:Job().ContinueWith(xxxTask),在異步任務完成后調用它的ContinueWith方法,參數是對任務完成與否的處理方法。
GC
.NET中將引用對象分為三類,第0代,第1代,第2代對象,一般會優先進行第0代回收, 然后將未成功回收的放到第1代中,如果第1代對象再次未回收成功會放到第2代中,這樣做可以減少回收的成本,提高效率。
在C#中,析構函數(也稱為終結器)主要用于釋放非托管資源(如文件句柄、數據庫連接、操作系統句柄等)。
class ResourceWrapper
{~ResourceWrapper() // 析構函數{// 釋放非托管資源CloseHandle(fileHandle);}
}
重要配置
在項目文件xxx.csproj中修改以下配置
工作站與服務器模式
工作站模式:適合內存占用小的程序和桌面應用,回收頻率高,是單線程的。
服務器模式:適合內存占用大的程序,回收頻率低,吞吐量高,是多線程的。
1、true - 服務器模式,false - 工作站模式
<ServerGarbageCollection>true</ServerGarbageCollection>
普通GC與后臺GC
普通GC:會導致單次停頓時間變長(例如UI線程的卡死),但消耗的資源少,支持壓縮處理。
后臺GC:單次停頓時間短,但停頓的頻率多,消耗的資源多,不支持壓縮處理。
2、true - 啟動后臺GC,false - 普通GC
<ConcurrentGarbageCollection>true</ConcurrentGarbageCollection>