C# --- 單例類錯誤初始化 + 沒有釋放資源導致線程泄漏
- Background
- 原因分析
- 問題一: 錯誤初始化(使用了箭頭函數)
- 問題一: 沒有Dispose資源
Background
- 背景: service A的其中一個Api會向mq發送消息
- 問題:線上發現這個服務經常有幾百個線程在同時運行,懷疑是發生了線程泄漏
- 什么是線程泄漏:代碼產生了大量的不應該出現的線程,導致占用過多資源,嚴重影響系統性能
原因分析
經過排查發現了一下問題代碼
public class Dispatch
{public static Dispatch Instance => new Dispatch();private readonly Sender senderpublic Dispatch() { sender = new Sender();}
}public class Sender : IDisposable
{private readonly Task _recoveryTask;public Sender() { recoveryTask = Task.Factory.StartNew(new Action(this.ReceoveryTaskEntry), TaskCreationOptions.LongRunning)}private void RecoveryTaskEntry() {while (!this.Disposed){//impl}}public void Dispose() {_recoveryTask.Dispose()GC.SuppressFinalize(this); // 阻止終結器調用}
}
問題一: 錯誤初始化(使用了箭頭函數)
public static Dispatch Instance => new Dispatch();//等價于
public static Dispatch Instance()
{return new Dispatch();
}
以上代碼在 Dispatch.Insatnce
被調用時每次都會新建一個Dispatch實例,而Dispatch的構造方法里會創建并運行一個新的線程,也就是說每個requets都會創建一個新的線程
正確的初始化:保證單例類只有一個實例
public static Dispatch Instance { get; } = new Dispatch();
問題一: 沒有Dispose資源
在Dispatch中沒有dispose sender, 導致線程沒有被釋放
public class Dispatch
{public static Dispatch Instance => new Dispatch();private readonly Sender senderpublic Dispatch() { sender = new Sender();}
}
正確實現:在Dispose中釋放資源
public class Dispatch
{public static Dispatch Instance => new Dispatch();private readonly Sender senderpublic Dispatch() { sender = new Sender();}public void Dispose() {sender.Dispose()//....}
}