缓冲/无缓冲去频道和死锁

时间:2017-04-11 07:25:25

标签: go goroutine

为了学习,我使用这段代码与goroutines一起玩。我无法得到不同的东西:

package main

import (
    "fmt"
    "time"
)

func main() {
    ch1 := make(chan string) // <A>
    ch2 := make(chan string) // <B>

    go test(ch1, ch2)
    ch1 <- "hi" // <C>
    ch2 <- "bye" // <D>
    fmt.Scanln()
}

func test(ch1 chan string, ch2 chan string) {
    timeout := time.After(time.Second * 2) // <E>
    defer func() { fmt.Println("returning") }()
    for {
        select {
        case s := <-ch1:
            fmt.Println(s, "1")
            ch2 <- "ch2"
            fmt.Println("after 1")
        case s := <-ch2:
            fmt.Println(s, "2")
            ch1 <- "ch1"
            fmt.Println("after 2")
        case <-timeout:
            fmt.Println("timed out")
            return
        }
    }
}

如果我按原样运行代码,我总是得到:

  

嗨1   致命错误:所有goroutines都睡着了 - 死锁!

关键是程序正好等待 E 部分的指定持续时间。我的意思是,如果我增加睡眠时间,那个致命的错误会在经过那段时间之后出现。所以第一个问题是:

1-究竟发生了什么?谁能解释一下这种行为?

1-1-为什么代码总是打印"hi 1"?我已经读过select语句随机从准备好的频道中选择,为什么总是"hi 1"?如果我交换 C D ,则会始终打印"bye 2"

1-2-为什么程序会等待那段时间然后发生死锁?

现在假设我将通道缓冲在 A B 中,大小为1,即:

ch1 := make(chan string, 1) // <A>
ch2 := make(chan string, 1) // <B>

现在每次运行程序时,它会随机打印"hi 1""bye 2"(只有一次)并永远等待(如果我点击进入程序退出,在main函数中编码)

2-现在发生了什么?请解释一下。

最后,如果我将缓冲区大小设置为2(或更多):

ch1 := make(chan string, 2) // <A>
ch2 := make(chan string, 2) // <B>

程序按预期运行,并依次打印"hi 1""bye 2",直到经过 E 部分编码的时间为止:

ch1 1  
after 1  
ch2 2  
after 2  
ch1 1  
after 1  
ch1 1  
after 1  
ch2 2   
.  
.  
.  
timed out  
returning  

我认为这里的一切都很清楚,因为通道的缓冲大小合适,一切都按预期工作。

1 个答案:

答案 0 :(得分:0)

  1. 想想一旦所有频道发送内容完成后会发生什么:你选择在无休止的for循环中运行,一旦定时器被触发就没有任何东西可以运行。
  2. 1.1。你首先发送ch1。 select循环从可以运行的所有路径中随机选择。第二种情况无法运行,因为在ch1中“hi”之前,ch2中没有任何内容。

    1.2。你的超时。

    1. 您的频道已缓存,即使没有人直接从他们那里阅读,您也可以将其发送给他们。因此,可以执行语句ch1<-"hi"和`ch2&lt; - “by2”,并且select可以选择。