在Go語言中,利用goroutine實現并發任務處理時,常常需要限制協程數量以防止資源耗盡。然而,不當的限制機制可能導致死鎖。本文將探討如何在限制協程數量的同時,有效避免死鎖,并確保從協程中順利接收數據。
問題描述:
使用sync.WaitGroup和通道c來限制同時運行的協程數量,但將數據發送到另一個通道creport時,出現死鎖錯誤:“fatal Error: all goroutines are asleep – deadlock!”。
立即學習“go語言免費學習筆記(深入)”;
錯誤代碼示例(簡化版):
(原文提供的代碼示例存在邏輯錯誤,此處提供一個更清晰的簡化版,以便說明問題和解決方案)
package main import ( "fmt" "sync" ) func worker(id int, c chan struct{}, creport chan int) { c <- struct{}{} // 獲取令牌,進入處理 defer func() { <-c }() // 釋放令牌,離開處理 // 模擬任務處理 result := id * 2 creport <- result } func main() { c := make(chan struct{}, 3) // 限制同時運行3個協程 creport := make(chan int) var wg sync.WaitGroup for i := 0; i < 10; i++ { wg.Add(1) go func(i int) { defer wg.Done() worker(i, c, creport) }(i) } wg.Wait() // 等待所有協程完成 close(creport) // 關閉creport通道,防止死鎖 for result := range creport { fmt.Println("Result:", result) } }
死鎖原因分析:
此例中,creport通道未被關閉,main函數中的for…range creport語句試圖從一個未關閉的通道中讀取數據。如果所有worker goroutine都已完成,但main函數仍在等待creport通道中的數據,就會發生死鎖。
解決方案:
為了避免死鎖,必須在所有worker goroutine完成任務后,關閉creport通道。 wg.Wait()確保所有協程都已完成,之后再關閉creport。 修改后的代碼如下:
package main import ( "fmt" "sync" ) // ... (worker function remains the same) ... func main() { // ... (channel creation remains the same) ... for i := 0; i < 10; i++ { wg.Add(1) go func(i int) { defer wg.Done() worker(i, c, creport) }(i) } wg.Wait() // 等待所有協程完成 close(creport) // 關閉creport通道 for result := range creport { fmt.Println("Result:", result) } }
通過在wg.Wait()之后關閉creport通道,for…range creport循環能夠正常結束,避免了死鎖。 這確保了所有數據都被正確接收,并且不會發生死鎖。 記住,正確地管理通道的關閉對于避免Go語言中的死鎖至關重要。
? 版權聲明
文章版權歸作者所有,未經允許請勿轉載。
THE END