為什么加了鎖的代碼偶爾還會導致panic: send on closed channel?

為什么加了鎖的代碼偶爾還會導致panic: send on closed channel?

go語言加鎖代碼偶爾出現panic: send on closed channel的原因分析

在Go語言并發編程中,使用鎖(mutex)保證線程安全是常見做法,但即使使用了鎖,仍然可能遇到panic: send on closed channel錯誤。本文分析此問題出現的原因及解決方案。

問題代碼及現象

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

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.Background())      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")             }         }(i)     }     wg.Wait() }

盡管使用了lock.Lock()和lock.Unlock()保護臨界區,但程序仍然可能在c

問題分析

Go語言select語句具有非確定性:如果多個case都準備好接收或發送,select會隨機選擇一個執行。

關鍵在于:

  1. close(c)和c close(c)操作和c

  2. select語句的隨機性: 即使ctx.Done()已經準備好,select仍然可能隨機選擇c

解決方案

為了避免此問題,需要確保在發送數據前檢查通道是否已關閉。 可以使用select語句的默認case來實現:

select { case c <- i:     fmt.Printf("sent %dn", i) default:     fmt.Println("channel closed or full") }

或者,使用一個額外的通道來協調關閉操作:

package main  import (     "fmt"     "sync" )  func main() {     c := make(chan int, 10)     done := make(chan struct{})     wg := sync.WaitGroup{}      wg.Add(1)     go func() {         defer wg.Done()         close(done) // Signal that the channel is closing         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 <-done:                 fmt.Println("channel closing")             }         }(i)     }     wg.Wait() }

這個改進的版本使用done通道來通知goroutine通道即將關閉,避免了競爭條件。

通過以上方法,可以有效地避免panic: send on closed channel錯誤,即使在并發環境下使用鎖。 選擇哪種解決方案取決于具體的應用場景和代碼復雜度。

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