在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 分享