在 goroutines 中使用 select 语句的通道通信行为
Channel communication behaviour with select statement in goroutines
这是 "A Tour of Go" 提供的版本的修改版本。
package main
import "fmt"
func fibonacci(c, quit chan int) {
x, y := 0, 1
for {
select {
case c <- x:
x, y = y, x+y
fmt.Println("GEN", x)
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("DISP", <-c)
}
quit <- 0
}()
fibonacci(c, quit)
}
以上代码的输出如下:
DISP 0
GEN 1
GEN 1
DISP 1
DISP 1
GEN 2
GEN 3
DISP 2
DISP 3
GEN 5
GEN 8
DISP 5
DISP 8
GEN 13
GEN 21
DISP 13
DISP 21
GEN 34
GEN 55
DISP 34
quit
我不明白代码的行为。为什么在显示它们之前生成了 2 个 Fibonacci 数?这是否取决于执行环境?
这与运行时如何调度 goroutines 有关。首先通过在每次推送到通道 c 后添加延迟,我们得到如下所示的正确结果:
package main
import (
"fmt"
"time"
)
func fibonacci(c, quit chan int) {
x, y := 0, 1
for {
select {
case c <- x:
time.Sleep(1 * time.Millisecond)
x, y = y, x+y
fmt.Println("GEN", x)
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("DISP", <-c)
}
quit <- 0
}()
fibonacci(c, quit)
}
以上代码的结果是:
DISP 0
GEN 1
DISP 1
GEN 1
DISP 1
GEN 2
DISP 2
GEN 3
DISP 3
GEN 5
DISP 5
GEN 8
DISP 8
GEN 13
DISP 13
GEN 21
DISP 21
GEN 34
DISP 34
GEN 55
quit
去游乐场link:https://play.golang.org/p/QD5kyGXWoJk
通过在通道 c 上的每次发送后添加延迟,我们调用 goroutine 调度程序将当前 goroutine 放入等待队列,从而允许其他 goroutine 被调度并成功打印消息。 Golang 有协作调度,所以 goroutines 不会立即被抢占。
因为goroutine(go func() {...}
)中的接收者在从发送者(func fibonacci()
中的select语句)接收到值时并发执行。
通过在发送值后添加延迟(c <- x:
) 将避免同时发送多个,这样您可以看得更清楚。
试试这个代码:(https://play.golang.org/p/9rOdS2YThKR)
package main
import (
"fmt"
"time"
)
func fibonacci(c, quit chan int) {
x, y := 0, 1
for {
fmt.Println("-----------------------------------------")
fmt.Println("current x:", x)
select {
//Send a value into a channel using the c <- x syntax, block until receiver is ready
case c <- x:
//When receiver gets x value, this code will executes
//Delay, avoid mutilple sending at the same time
time.Sleep(5 * time.Millisecond)
//Increase
x, y = y, x+y
fmt.Println("increased x to", x)
case <-quit:
fmt.Println("quit")
return
}
}
}
func main() {
c := make(chan int)
quit := make(chan int)
go func() {
//The <-c syntax receives a value from the channel, block until sender is ready
for i := 0; i < 10; i++ {
fmt.Println("received x:", <-c)
}
quit <- 0
}()
fibonacci(c, quit)
}
以上代码的结果是:
-----------------------------------------
current x: 0
received x: 0
increased x to 1
-----------------------------------------
current x: 1
received x: 1
increased x to 1
-----------------------------------------
current x: 1
received x: 1
increased x to 2
-----------------------------------------
current x: 2
received x: 2
increased x to 3
-----------------------------------------
current x: 3
received x: 3
increased x to 5
-----------------------------------------
current x: 5
received x: 5
increased x to 8
-----------------------------------------
current x: 8
received x: 8
increased x to 13
-----------------------------------------
current x: 13
received x: 13
increased x to 21
-----------------------------------------
current x: 21
received x: 21
increased x to 34
-----------------------------------------
current x: 34
received x: 34
increased x to 55
-----------------------------------------
current x: 55
quit
这是 "A Tour of Go" 提供的版本的修改版本。
package main
import "fmt"
func fibonacci(c, quit chan int) {
x, y := 0, 1
for {
select {
case c <- x:
x, y = y, x+y
fmt.Println("GEN", x)
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("DISP", <-c)
}
quit <- 0
}()
fibonacci(c, quit)
}
以上代码的输出如下:
DISP 0
GEN 1
GEN 1
DISP 1
DISP 1
GEN 2
GEN 3
DISP 2
DISP 3
GEN 5
GEN 8
DISP 5
DISP 8
GEN 13
GEN 21
DISP 13
DISP 21
GEN 34
GEN 55
DISP 34
quit
我不明白代码的行为。为什么在显示它们之前生成了 2 个 Fibonacci 数?这是否取决于执行环境?
这与运行时如何调度 goroutines 有关。首先通过在每次推送到通道 c 后添加延迟,我们得到如下所示的正确结果:
package main
import (
"fmt"
"time"
)
func fibonacci(c, quit chan int) {
x, y := 0, 1
for {
select {
case c <- x:
time.Sleep(1 * time.Millisecond)
x, y = y, x+y
fmt.Println("GEN", x)
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("DISP", <-c)
}
quit <- 0
}()
fibonacci(c, quit)
}
以上代码的结果是:
DISP 0
GEN 1
DISP 1
GEN 1
DISP 1
GEN 2
DISP 2
GEN 3
DISP 3
GEN 5
DISP 5
GEN 8
DISP 8
GEN 13
DISP 13
GEN 21
DISP 21
GEN 34
DISP 34
GEN 55
quit
去游乐场link:https://play.golang.org/p/QD5kyGXWoJk
通过在通道 c 上的每次发送后添加延迟,我们调用 goroutine 调度程序将当前 goroutine 放入等待队列,从而允许其他 goroutine 被调度并成功打印消息。 Golang 有协作调度,所以 goroutines 不会立即被抢占。
因为goroutine(go func() {...}
)中的接收者在从发送者(func fibonacci()
中的select语句)接收到值时并发执行。
通过在发送值后添加延迟(c <- x:
) 将避免同时发送多个,这样您可以看得更清楚。
试试这个代码:(https://play.golang.org/p/9rOdS2YThKR)
package main
import (
"fmt"
"time"
)
func fibonacci(c, quit chan int) {
x, y := 0, 1
for {
fmt.Println("-----------------------------------------")
fmt.Println("current x:", x)
select {
//Send a value into a channel using the c <- x syntax, block until receiver is ready
case c <- x:
//When receiver gets x value, this code will executes
//Delay, avoid mutilple sending at the same time
time.Sleep(5 * time.Millisecond)
//Increase
x, y = y, x+y
fmt.Println("increased x to", x)
case <-quit:
fmt.Println("quit")
return
}
}
}
func main() {
c := make(chan int)
quit := make(chan int)
go func() {
//The <-c syntax receives a value from the channel, block until sender is ready
for i := 0; i < 10; i++ {
fmt.Println("received x:", <-c)
}
quit <- 0
}()
fibonacci(c, quit)
}
以上代码的结果是:
-----------------------------------------
current x: 0
received x: 0
increased x to 1
-----------------------------------------
current x: 1
received x: 1
increased x to 1
-----------------------------------------
current x: 1
received x: 1
increased x to 2
-----------------------------------------
current x: 2
received x: 2
increased x to 3
-----------------------------------------
current x: 3
received x: 3
increased x to 5
-----------------------------------------
current x: 5
received x: 5
increased x to 8
-----------------------------------------
current x: 8
received x: 8
increased x to 13
-----------------------------------------
current x: 13
received x: 13
increased x to 21
-----------------------------------------
current x: 21
received x: 21
increased x to 34
-----------------------------------------
current x: 34
received x: 34
increased x to 55
-----------------------------------------
current x: 55
quit