我试图了解goroutine,select和channel并发背后的逻辑。示例代码如下。基本代码来自tour go。我添加一些Printf以帮助我更好地理解。
package main
import "fmt"
func fibonacci(c, quit chan int) {
x, y := 0, 1
for {
select {
case c <- x:
fmt.Printf("(%v, %v)\n", x ,y)
x, y = y, x+y
fmt.Printf("(%v, %v)\n", x ,y)
case <-quit:
fmt.Println("quit")
return
}
}
}
func main() {
c := make(chan int)
quit := make(chan int)
go func() {
for i := 0; i < 4; i++ {
fmt.Println(<-c)
fmt.Printf("%v from main\n",i)
}
quit <- 0
}()
fibonacci(c, quit)
}
输出为
0
0 from main
(0, 1)
(1, 1)
(1, 1)
(1, 2)
1
1 from main
1
2 from main
(1, 2)
(2, 3)
(2, 3)
(3, 5)
2
3 from main
quit
goroutine和通道操作之间存在并发性。我的问题是为什么输出不是
0
0 from main
(0, 1)
(1, 1)
1
1 from main
(1, 1)
(1, 2)
1
2 from main
(1, 2)
(2, 3)
2
3 from main
(2, 3)
(3, 5)
quit
答案 0 :(得分:1)
它可能会澄清一些事情,以便稍微重写goroutine中的for循环主体:
x := <-c
// block until a value has been read, then continue
fmt.Println(x)
fmt.Printf("%v from main\n",i)
fibonacci()
中的select循环主体实际上是有效的:
c <- x
// block until a value has been written, then continue
fmt.Printf("(%v, %v)\n", x ,y)
x, y = y, x+y
fmt.Printf("(%v, %v)\n", x ,y)
在两种情况下,您都可以保证第二个打印语句将在第一个打印语句之后打印。
运行程序时,说goroutine立即启动。它达到了此读取的目的,但是通道中没有任何值,因此将其阻塞。然后,主程序调用{{1}}。它进入选择语句。 fibonacci()
频道有一个阅读器,因此它在其中发送数字c
。
一旦发生这种情况,两者都可以自由运行goroutine和主程序。 select语句已触发其分支之一并发送了其值;读取已完成。这两个goroutine都可以自由运行,并且可以按任何顺序运行(只要它们按顺序执行自己的语句即可)。最终,您将达到goroutine在读取时被阻止或x
在select上被阻止的地步,一旦两者相遇,它们将可以再次自由执行。
您提出的顺序将要求读者在作者之前先“醒来”,但Go对此并没有要求,实际上在多核系统上,两者都可以同时运行。