写入频道永远被阻止

时间:2017-01-21 17:08:43

标签: go deadlock channel goroutine

我陷入了一种奇怪的情况,即对频道的写操作永远不会发生。

package main

import (
    "fmt"
    "time"
)

func main() {
    c := make(chan int)
    s := make(chan bool)
    k := make(chan bool)
    fmt.Println("start")
    go func() {
    fmt.Println("routine 1")
        s <- true
    }()
    go func() {
    fmt.Println("routine 2")
        for {
            select {
            case  <-s :
                for i := 0; i < 3; i++ {
                    fmt.Println("before")
                    c <- i
                    fmt.Println("after")
                }       
            case <- k :
                fmt.Println("k ready")
                break
            }
        }
    }()

    go func() {
        fmt.Println("routine 3")
        for {
            select {
                case x := <- c :
                fmt.Println("x=", x)
                k <- true
                fmt.Println("k done")

            }
        }
    }()

    time.Sleep(1000 * time.Millisecond)
}

这是输出:

start
routine 1
routine 2
before
routine 3
x= 0
after
before

我想知道为什么写入通道k阻塞,但是从不打印日志语句fmt.Println("k ready")

以下是我的想法:

  • go例程1写入频道s
  • go例程2写0到 通道c和等待,因为缓冲区大小为0,它将无法 写&#39; 1&#39;除非有人读取频道c
  • go例程3进入图片,读取通道c(现在进入例程2 可以写入c一旦进入例程2恢复)打印x的值。 现在应该写信给K,但是没有发生

据我说它应该能够写入通道k然后goroutine的情况2应该执行并打印&#34; k ready&#34;

任何人都可以解释为什么写入频道被阻止? 作为一个修复,我知道我可以增加通道c的缓冲区大小,所有内容都会打印出来,但我不想修复它,而是想了解这种情况。

一个很好的blog来理解上述情况。

1 个答案:

答案 0 :(得分:1)

你有一个僵局。

  • goroutine 1写入s然后退出
  • goroutine 2从s读取,并写入c
  • goroutine 3从c读取,并写入k,这会阻止,因为没有任何内容正在从k读取,因为goroutine 2在写入k时被阻止上方。
  • goroutine 2再次向c写入阻止,因为goroutine 3仍在尝试写入k,因此不会从c
  • 读取

与您的说法相反,您的缓冲区大小不是1.您的缓冲区大小为零(即无缓冲通道),因此写入将阻塞,直到读取内容为止。这可能是你误解的根源。根据{{​​3}}:

  

可以使用内置函数make创建一个新的初始化通道值,该函数将通道类型和可选容量作为参数:

make(chan int, 100)
     

容量(以元素数量)设置通道中缓冲区的大小。如果容量为零或不存在,则通道无缓冲,只有当发送方和接收方都准备就绪时,通信才会成功。否则,如果缓冲区未满(发送)或非空(接收),则通道被缓冲并且通信成功而不会阻塞。零通道从未准备好进行通信。