golang中channel如何在多个goroutine中优雅(安全)的send

同一个channel, 我需要在多个goroutine中send, 在一个goroutine中receive, 并且我可以随时关闭这个channel, 问题是如果我关闭channel后还有send, 就会导致一个panic.

网上找到的一个方法是panic后再recover

func SafeSend(ch chan T, value T) (closed bool) {
    defer func() {
        if recover() != nil {
            // the return result can be altered 
            // in a defer function call
            closed = true
        }
    }()

    ch <- value // panic if ch is closed
    return false // <=> closed = false; return
}

因为不想用recover,问一下还有没有更加优雅的方式去实现

阅读 8k
4 个回答

通过一个chan来通知这些生产者退出,用WaitGroup等待这些生产者退出,最后关闭这个主chan

同一个channel, 需要在多个goroutine中send的话应该用缓冲区chan。

recover是尽量不用的,因此,应该在sender之前就做判断

题主应该是之前用过一段时间的其他语言,并且接触go不久,因此在错误处理的思路上,还不太符合go的特点。

与大多数语言不同,用try...catch...等机制来做错误处理,go建议大家不要轻易使用recover,而是用返回err对象来处理。因此,在可能出现panic的语句之前,尽量用能够返回err的函数去检查可能出现的错误!

最后我是用了另外一个chan去控制

func (mc *mgmtConn) sendData(data *[]byte) {
    select {
    case <-mc.stop:
        return
    case mc.send <- *data:
    }
}

当我关闭stop时, 就不会再send了
因此我不需要再关闭send,而是让系统去回收它

可以另外增加一个chan在关闭之前通知生产者结束任务

var msgs = make(chan int)
var done = make(chan struct{})

func main() {
    go send(msgs)
    go receive(msgs)

    time.Sleep(5 * time.Second)
}

func send(ch chan int) {
    for {
        <-time.After(1 * time.Second)

        select {
        case <-done:
            fmt.Println("done")
            return
        case ch <- time.Now().Second():
        }

    }

}

func receive(ch chan int) {
    <-time.After(1 * time.Second)

    msg := <-ch
    fmt.Println(msg)

    // 关闭前通知结束
    done <- struct{}{}
    close(ch)
}
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题