V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
xing393939
V2EX  ›  Go 编程语言

Go 读写一个 nil 通道会使协程永久挂起,为什么这样设计呢?

  •  
  •   xing393939 ·
    xing393939 · 2022-05-18 14:41:19 +08:00 · 1642 次点击
    这是一个创建于 702 天前的主题,其中的信息可能已经有所发展或是发生改变。

    写了一个 demo: https://go.dev/play/p/51-Z_XNr-th

    这里写一个 nil 通道会一直阻塞,Go 源码在这一行: https://hub.fastgit.xyz/golang/go/blob/go1.16.10/src/runtime/chan.go#L163

    不知道为什么要这样设计?一直阻塞会导致协程泄漏,还不如直接 panic 掉,所以官方为什么这样做呢

    .

    .

    .

    4 条回复    2022-05-18 15:05:54 +08:00
    pathletboy
        1
    pathletboy  
       2022-05-18 14:48:27 +08:00
    你贴的这个直接运行
    fatal error: all goroutines are asleep - deadlock!

    goroutine 1 [chan send (nil chan)]:
    main.main()
    /tmp/sandbox3073028740/prog.go:11 +0x7e

    goroutine 5 [chan receive]:
    main.main.func1()
    /tmp/sandbox3073028740/prog.go:9 +0x4f
    created by main.main
    /tmp/sandbox3073028740/prog.go:7 +0x6a

    Program exited.
    pathletboy
        2
    pathletboy  
       2022-05-18 14:51:25 +08:00
    https://go.dev/ref/spec#Channel_types
    A nil channel is never ready for communication.
    zzn
        3
    zzn  
       2022-05-18 15:03:22 +08:00   ❤️ 1
    > It's for consistency with select. The semantics of a nil channel are
    the same regardless of how it is used. It's useful that it blocks in a
    select, so that's what it does outside a select.

    > If it panicked outside a select, not only would it be inconsistent but
    the channel code would need to behave differently in the two cases, a
    needless complexity.

    https://groups.google.com/g/golang-nuts/c/QltQ0nd9HvE/m/VvDhLO07Oq4J
    pathletboy
        4
    pathletboy  
       2022-05-18 15:05:54 +08:00
    为了灵活性
    go 中,可以对 nil 值 channel 进行读写操作,当对值为 nil 的 channel 进行读取操作时会阻塞,但是对值为 nil 值的 channel 调用 close()会 panic 。使用 nil 值 channel 可以帮助在 select 中禁用某个 select 分支,因为阻塞了所以都不会进入分支语句。

    下面是 client-go 中对 nil 值 channel 和单向 channel 的使用的函数代码:

    func (p *processorListener) pop() {
    defer utilruntime.HandleCrash()
    defer close(p.nextCh) // Tell .run() to stop

    var nextCh chan<- interface{}
    var notification interface{}
    for {
    select {
    case nextCh <- notification:
    // Notification dispatched
    var ok bool
    notification, ok = p.pendingNotifications.ReadOne()
    if !ok { // Nothing to pop
    nextCh = nil // Disable this select case
    }
    case notificationToAdd, ok := <-p.addCh:
    if !ok {
    return
    }
    if notification == nil { // No notification to pop (and pendingNotifications is empty)
    // Optimize the case - skip adding to pendingNotifications
    notification = notificationToAdd
    nextCh = p.nextCh
    } else { // There is already a notification waiting to be dispatched
    p.pendingNotifications.WriteOne(notificationToAdd)
    }
    }
    }
    }
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   2801 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 12:34 · PVG 20:34 · LAX 05:34 · JFK 08:34
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.