golang为什么不能在主线程上将值传递给通道

golang why can not pass value to channel on the main thread

案例1

package main

func main()  {
    dogChan := make(chan int)
    dogChan <- 1
}
fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan send]:
main.main()
    /Users/xuzhongwei/Source/awesomeProject/main.go:5 +0x50

案例2

package main

func main()  {
    dogChan := make(chan int)

    go func(ch chan int) {
        
    }(dogChan)
    dogChan <- 1
}
fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan send]:
main.main()
    /Users/xuzhongwei/Source/awesomeProject/main.go:9 +0x72

案例 3

package main

func main()  {
    dogChan := make(chan int)

    go func(ch chan int) {
        <- ch
    }(dogChan)
    dogChan <- 1
}

案例4

package main

func main()  {
    dogChan := make(chan int)
    
    go func(ch chan int) {
        <- ch
    }(dogChan)
    dogChan <- 1
    dogChan <- 2
}
fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan send]:
main.main()
    /Users/xuzhongwei/Source/awesomeProject/main.go:10 +0x90

案例5

package main

func main()  {
    dogChan := make(chan int)

    go func(ch chan int) {
        for {
            select {
                case <- ch:
            }
        }

    }(dogChan)
    dogChan <- 1
    dogChan <- 2
    dogChan <- 3
    dogChan <- 4
    dogChan <- 5
}

谁能告诉我为什么 case1、case2 出错而 case3 正常? 在 case1 中,我的猜测是 dogChan 没有在 goroutine 中使用,所以它被视为关闭。 在 case2 中,我的猜测是虽然 dogChan 在 goroutine 中传递但它没有在 goroutine 中使用所以它被视为关闭

谁能告诉我为什么 case4 出错而 case5 正常?

为什么您认为 case1case2 会发生这种情况?这些通道旨在充当发送方和接收方之间的同步原语。您有一个发件人在频道上发送,dogChan 但 none 正在接收。 没有接收 goroutine 或 goroutine 上的接收操作,发送方只是阻塞(作为一个无缓冲的通道)

同样的问题 case4,你在通道上有两个发送,但在 goroutine 上有一个接收。 dogChan <- 2 将永远阻塞。在 case5 中,如果您的目的是从通道读取,只需使用 range 循环来迭代通过它发送的连续值。

Golang 期望程序读取放置在通道中的消息。

消费者 (reader) 需要从通道中排出(读取)所有消息,使用简单的 for-read 或 for-select。通道发送和接收都阻塞,直到发送方和接收方准备就绪。

  • case1,case2 = 向通道发送一条消息,阻止等待 reader,读取零条消息
  • case4 = 向通道发送一条消息,阻塞等待 reader,reader 不消耗(读取)消息
  • case3 = 向通道发送一条消息,从通道消费一条消息,发送方块等待 reader
  • case5 = 向通道发送五个消息,消耗所有(五个)消息,每个发送块直到 reader 收到
    // for range over channel
    for msg := range ch {
        // process msg
    }
    
    // for select
    done := false
    for !done {
        select {
            case msg := <-ch: {
                // process msg
            }
            case ch == nil: {
                done = true
            }
        }
    }
    
    // producer should close channel
    close(ch)

注:

  • 通道可以缓冲,指定通道(队列)大小
  • 通道大小默认 = 1(无缓冲),写入器在通道已满时阻塞