golang select channel问题

先上代码,是Go by Example的例子演化的:

// go 1.9
package main

import "fmt"
import "time"

func main() {
    messages := make(chan string)

    go func() {
        select {
        case msg := <-messages:
            fmt.Println("received message", msg)
        default:
            fmt.Println("no message received")
        }
    }()

    msg := "hi"
    select {
    case messages <- msg:
        fmt.Println("sent message", msg)
    // default:  // 问题所在
    //    fmt.Println("no message sent")
    }

    time.Sleep(time.Second)
}

注释default的代码块时打印:

received message hi
sent message hi

取消注释后一直打印:

no message sent
no message received

问题是取消注释为什么不打印:

received message hi
sent message hi

毕竟case messages <- msg一直是有效的呀.

阅读 4.8k
4 个回答

跟这两个goroutine的执行顺序有关即其中的select条件是否就绪有关.

messages := make(chan string)

因为这样定义的channel是阻塞的,类似于我们平常的买票窗口,需要传递和接收同时进行才能完成一次交互,否则传递或接收都会处于等待状态。

由于go新建一个goroutine需要一点时间,主线程走到select块的时候接收的goroutine一般都还没运行起来

1.主线程select块没有default情况下

case messages <- msg:

这句才会生效进入阻塞状态

2.主线程select块有default情况下,由于接收线程没有准备好,所以直接执行default块,忽略了

case messages <- msg:

如果想要达到想要的效果可以在主线程select之前加入等待一段时间的代码即可

msg := "hi"
// 模拟main线程阻塞
time.Sleep(100 * time.Millisecond)
select {
case messages <- msg:
    fmt.Println("sent message", msg)
default:
    fmt.Println("no message sent")
}

在第二个 select 外面加一个 for{} 试试。

select是这么解释的:当 select 中的其他条件分支都没有准备好的时候,default 分支会被执行。

可能是 messages <- msg 还没准备好吧

问题的关键在于 messages 为无缓存chanel,发送和接收双方必须同时都准备好才行。
把它改为带缓存的就很好理解了。

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题