【发布时间】:2017-09-06 10:50:11
【问题描述】:
为了学习,我使用这段代码来玩 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
致命错误:所有 goroutine 都处于休眠状态 - 死锁!
关键是程序完全等待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"(仅一次)并永远等待(如果我按主函数中的代码输入程序退出)
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
我认为这里一切都很清楚,因为通道以适当的大小缓冲,所以一切都按预期工作。
【问题讨论】:
-
不确定您要在这里实现什么。这个程序应该提前退出,因为没有任何东西阻塞主函数。我认为这不是预期的行为。
-
程序不会退出,因为在 main 函数的末尾调用了 fmt.Scanln。