上下文:https://tour.golang.org/concurrency/5
大家好,我正在按照上面的链接学习Go。
描述说:“如果准备好多个,则随机选择一个。” 但是,在使主例程等待2秒之后,才调用func fibonacci。 2秒后,频道应为以下频道: c:10次致电以从渠道中获取价值 退出:0
在我看来,两个渠道都已准备就绪。如果“如果有多个准备好,它将随机选择一个”为true,那么斐波纳契中对该案例的首次调用将有50%的机会从退出渠道获得0。但是,事实并非如此。退出之前,所有10个数字始终会被打印出来。因此,看起来选择不是随机的。我想念什么吗?
package main
import "fmt"
import "time"
func fibonacci(c, quit chan int) {
x, y := 0, 1
for {
select {
case c <- x:
x, y = y, x+y
case <-quit:
fmt.Println("quit")
return
}
}
}
func main() {
c := make(chan int)
quit := make(chan int)
go func() {
for i := 0; i < 10; i++ {
fmt.Println(<-c)
}
quit <- 0
}()
time.Sleep(2 * time.Second)
fibonacci(c, quit)
}
此外,下一页: https://tour.golang.org/concurrency/6
看起来默认代码应该打印出两个刻度。或BOOM!以500毫秒为单位。但是,只有BOOM!总是打印。如果我将默认时间从50更改为55,则同时打勾和BOOM。为什么是这样?在选择中,“ After”优先于“ Tick”吗?
package main
import (
"fmt"
"time"
)
func main() {
tick := time.Tick(100 * time.Millisecond)
boom := time.After(500 * time.Millisecond)
for {
select {
case <-tick:
fmt.Println("tick.")
case <-boom:
fmt.Println("BOOM!")
return
default:
fmt.Println(" .")
time.Sleep(55 * time.Millisecond)
}
}
}
答案 0 :(得分:1)
在代码的第一段中,quit
在for
循环之前从未准备就绪。并且,在循环的每次迭代中,c
都准备就绪,并且将阻塞直到发送数字为止。因此,实际上select
除了写c
之外什么也不能做。睡觉两秒钟根本没关系。
第二段代码未出错。 select
确实确实随机选择了一个案例。但是,Go Playground具有固定的随机数生成器,这意味着在Playground上,select每次运行总是会选择一种情况。
答案 1 :(得分:1)
我认为您误会了主要方法中发生的事情...为了清楚起见,我将分解我认为正在发生的事情
func main() {
c := make(chan int)
quit := make(chan int) // make a couple channels
go func() {
for i := 0; i < 10; i++ {
fmt.Println(<-c) // blocked here in goroutine
}
quit <- 0
}() // go func { ... }() - you're both writing this closure and invoking it as a goroutine
time.Sleep(2 * time.Second) // sleep for 2 seconds
fibonacci(c, quit) // call fibonacci passing in the channels
}
所以这里实际发生的事情是,您将此闭合称为goroutine,然后等待2秒钟,在此期间您的goroutine仍位于for循环的主体中,等待在c
上接收,您调用{{ 1}}会按您期望的那样执行,进入for-select,这时您会在循环fibonacci
的每次迭代中不断命中该代码(它接收到,我得到递增,您再次接收到下一个值,直到由于i == 10,循环结束。然后您进入下一行并在quit通道上发送,则select执行该条件,然后程序退出。
就首先执行的语言规范而言;
“ select”语句的执行分几个步骤进行:
1)对于语句中的所有情况,receive的通道操作数 操作以及send的通道和右侧表达式 输入后,按源顺序对语句进行一次精确评估 “选择”语句。结果是一组接收通道 从或发送到,以及要发送的相应值。任何一侧 不论哪个(如果有),都会对该评估产生影响 选择通讯操作进行。上的表达式 具有简短变量声明的RecvStmt的左侧或 作业尚未评估。 2)如果一个或多个 通信可以进行,选择一个可以进行的通信 通过统一的伪随机选择。否则,如果有 默认情况下,选择该情况。如果没有默认情况, “选择”语句将阻塞,直到至少有一种通信可以 继续。除非所选案例是默认案例,否则各自 通信操作被执行。 3)如果所选案例是 带有简短变量声明或赋值的RecvStmt, 评估左侧表达式,并接收值(或 值)。 4)所选案例的陈述清单为 被执行。
在比赛条件下这只是伪随机,问题是您没有在创建比赛条件。
答案 2 :(得分:0)
您创建了无缓冲的chan
:
c := make(chan int)
这意味着从chan读取的所有内容都会被阻塞,直到有内容写入为止。而且,写给chan的所有内容都将被阻止,直到从中读取内容为止。
因此在此代码中:
for i := 0; i < 10; i++ {
fmt.Println(<-c)
}
quit <- 0
<-c
会阻塞,直到在c
上放了东西。因此,它一直坐在那里,直到您的time.Sleep()
完成后才到达:
case c <- x:
交替交替来回阻塞读取一个值,写入一个值,直到读取10个值,然后将0
发送到quit
。
要创建缓冲通道,您需要指定缓冲区的大小。
c := make(chan int, 10)
但是请注意,即使执行此操作,它仍然不会按照预期的方式运行,因为在写入quit
之前要读取所有10个值。您需要将作家放在同一位置,将读者放在同一位置,而不要混淆他们。