下面这段代码加了锁为什么偶尔还是会报panic?

作者站长头像
站长
· 阅读数 14

下面这段代码加了锁为什么偶尔还是会报:panic: send on.closed channel

package main

import (
    "context"
    "fmt"
    "sync"
)

var lock sync.Mutex

func main() {
    // channel 初始化
    c := make(chan int, 10)
    // 用来 recevivers 同步事件的
    wg := sync.WaitGroup{}
    // 上下文
    ctx, cancel := context.WithCancel(context.TODO())

    // 专门关闭的协程
    wg.Add(1)
    go func() {
        defer wg.Done()
        lock.Lock()

        cancel()
        // ... 某种条件下,关闭 channel
        close(c)
        lock.Unlock()
    }()

    // senders(写端)
    for i := 0; i < 10; i++ {

        wg.Add(1)
        go func(ctx context.Context, id int) {
            defer wg.Done()
            lock.Lock()
            select {
            case <-ctx.Done():
                fmt.Println("aaa")
                return
            case c <- id: // 入队
                // ...
            }
            lock.Unlock()
        }(ctx, i)
    }

    // 等待所有的 receivers 完成;
    wg.Wait()
    for i2 := range c {
        fmt.Println(i2)
    }

}


回复
1个回答
avatar
test
2024-07-20

https://go.dev/ref/spec#Selec...

If one or more of the communications can proceed, a single one that can proceed is chosen via a uniform pseudo-random selection.

https://go.dev/ref/spec#Send_...

A send on a buffered channel can proceed if there is room in the buffer. A send on a closed channel proceeds by causing a run-time panic.

case <-ctx.Done(): 并不能阻止 case c<-id: 的执行。因为即使 c 被关闭了,他依然 "can proceed" ,于是会在 <-ctx.Done()c<-id 中随机选择一个执行。

回复
likes
适合作为回答的
  • 经过验证的有效解决办法
  • 自己的经验指引,对解决问题有帮助
  • 遵循 Markdown 语法排版,代码语义正确
不该作为回答的
  • 询问内容细节或回复楼层
  • 与题目无关的内容
  • “赞”“顶”“同问”“看手册”“解决了没”等毫无意义的内容