golang中的死鎖

什麼是死鎖

死鎖是指兩個或兩個以上的程序在執行過程中,因爭奪資源而造成的一種互相等待的現象,若無外力作用,它們都將無法推進下去。

golang中的死鎖

golang 中的死鎖是當 goroutine 被阻塞而沒有任何可能被解除阻塞時發生的狀態。

死鎖檢測器

死鎖可能會導致程式異常,因此如果能夠在開發階段就發現死鎖可以大大避免程式的崩潰。

golang 提供了一個死鎖檢測器,可以幫助開發人員檢測出程式碼中寫出的死鎖程式碼。

golang中的死鎖

死鎖檢測器基於對應用程式建立的執行緒的進行分析,如果建立和活動的執行緒數高於等待工作的執行緒數,則會出現死鎖情況。

在檢測到死鎖時,會建立四個執行緒:

一個用於主 goroutine,啟動程式的那個。

一種監視系統的稱為 sysmon。

一種專用於垃圾收集器的 goroutines 啟動。

在初始化期間主 goroutine 被阻塞時建立的一個執行緒。

每次執行緒空閒時,都會通知檢測器。除錯的每一行都顯示增加的空閒執行緒數。當空閒執行緒數等於活動執行緒數減去系統執行緒數時,就會發生死鎖。

但是,這種行為有一些限制。實際上,任何自旋 goroutine 都會使死鎖檢測器變得無用,因為執行緒將保持活動狀態。

如果你想視覺化執行程式上的死鎖,可以使用 pprof 之類的工具來視覺化它。

產生死鎖的原因

goroutine 會產生死鎖,要麼是因為它正在等待管道訊息,要麼 是因為它正在等待同步包中的鎖。

當沒有其他 goroutine 可以訪問通道或鎖的時候,一組 goroutine 正在等待對方,但沒有一個能夠繼續,這時就會產生死鎖。

golang中的死鎖

目前,Go 只檢測整個程式何時凍結,而不檢測 goroutine 的子集何時產生死鎖。

使用管道通常很容易找出導致死鎖的原因。但是大量使用互斥鎖的程式卻很難除錯。

go-deadlock

我們知道對於管道的死鎖很容易檢測,但是對於互斥鎖卻很難除錯發現,因此,有人做出了go-deadlock死鎖檢測庫。

golang中的死鎖

這個死鎖檢測庫並不是基於靜態分析的,而是基於執行時檢測的。

原理很簡單,就是獲取當前協程的goroutine id,然後存了當前協程沒有釋放的lock的物件。 這時候當其他協程去lock的時候,會觸發prelock檢測,檢測有沒有衝突lock關係。