为什么加了锁的代码偶尔还会导致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 分享