在Go語言中,為什么即使加了鎖,仍然會出現“send on closed channel”的panic?

在Go語言中,為什么即使加了鎖,仍然會出現“send on closed channel”的panic?

go語言并發編程:鎖與通道關閉的陷阱

Go語言中,channel和mutex是處理并發問題的利器,但兩者結合使用時,容易出現意想不到的錯誤,例如本文要討論的“panic: send on closed channel”問題。即使使用了mutex鎖,仍然可能出現此錯誤。

問題重現

以下代碼片段演示了這個問題:

package main  import (     "context"     "fmt"     "sync" )  var lock sync.Mutex  func main() {     c := make(chan int, 10)     wg := sync.WaitGroup{}     ctx, cancel := context.WithCancel(context.TODO())      wg.Add(1)     go func() {         defer wg.Done()         lock.Lock()         cancel()         close(c)         lock.Unlock()     }()      for i := 0; i < 10; i++ {         wg.Add(1)         go func(i int) {             defer wg.Done()             select {             case c <- i:                 fmt.Printf("Sent: %dn", i)             case <-ctx.Done():                 fmt.Println("Context cancelled, exiting sender")             }         }(i)     }      wg.Wait() }

問題根源分析

代碼中,lock.Lock() 和 lock.Unlock() 保證了close(c)操作的原子性,防止多個goroutine同時關閉通道。然而,select語句的非確定性導致問題。即使通道c已關閉,case c

解決方案

為了避免panic,需要在發送數據前檢查通道是否關閉,或者使用上下文機制優雅地關閉goroutine。以下改進后的代碼使用上下文機制:

立即學習go語言免費學習筆記(深入)”;

package main  import (     "context"     "fmt"     "sync" )  func main() {     c := make(chan int, 10)     wg := sync.WaitGroup{}     ctx, cancel := context.WithCancel(context.TODO())      wg.Add(1)     go func() {         defer wg.Done()         cancel() // 先取消上下文         close(c)     }()      for i := 0; i < 10; i++ {         wg.Add(1)         go func(i int) {             defer wg.Done()             select {             case c <- i:                 fmt.Printf("Sent: %dn", i)             case <-ctx.Done():                 fmt.Println("Context cancelled, exiting sender")             }         }(i)     }      wg.Wait() }

此版本中,我們先取消上下文,再關閉通道。select語句中的case

以上就是在Go語言中,

? 版權聲明
THE END
喜歡就支持一下吧
點贊10 分享