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

新手上路,请多包涵

下面这段代码加了锁为什么偶尔还是会报: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.9k
2 个回答

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 中随机选择一个执行。

仅供参考,我对golang
  1. 第一个gorouting中,在某些情不 况下你 close(c) 会匹配到其他gorouting中的select c<– id case,因为此时channel已经关闭了,所以会panic
  2. 第一个gorouting中,你调用cancel()会匹配到其他gorouting中的select 中的 ctx.Done() case,然后执行return,这个时候会基本会导致死锁 ,defer lock.unlocke()
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题