这个select在goroutine中是如何工作的?



我一直在跟随go tour的例子,我不明白这是如何工作的https://tour.golang.org/concurrency/5

package main
import "fmt"
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
}()
fibonacci(c, quit)
}

这是如何工作的?

当我试着去理解。

package main
import "fmt"
func b(c,quit chan int) {
c <-1
c <-2
c <-3
}
func main() {
c := make(chan int)
quit := make(chan int)
go func() {
for i := 0; i < 10; i++ {
fmt.Println(<-c)
}
quit <- 0
}()
b(c,quit)
}

有时会打印1,2有时会打印1,2,3,为什么?

首先,在func fibonacci中,select语句尝试选择下面两个语句中的第一个来完成:

  1. c <- x
  2. <- quit

很容易理解<- quit,它试图从一个叫做quit的通道接收一个值(并且忽略接收到的值)。

c <- x意味着发送一个等于x的值(是x的副本)。这看起来像是解除阻塞,但在Go中,在没有接收者的情况下,通过一个未缓冲的通道(在Go指南中解释)发送阻塞。

所以这里它的意思是,等待接收者准备好接收值(或者缓冲区中的一个空间,如果它是一个缓冲通道),在这段代码中意味着fmt.Println(<-c),然后将值发送给接收者。

因此,当<-c被求值时,该语句解除阻塞(完成)。即循环的每次迭代。

对于您的代码,虽然所有值1,2,3都保证通过通道发送(并接收),但func b返回,因此func main保留而不保证fmt.Println(3)完成。

在Go中,当func main返回时,程序终止,未完成的例程没有机会完成它的工作-因此有时打印3,有时不打印。

为了更好地理解斐波那契的例子,让我们分析一下不同的部分。

首先是匿名函数
go func() {
for i := 0; i < 10; i++ {
fmt.Println(<-c)
}
quit <- 0
}()

"go"关键字将启动一个新的程序,所以现在我们有了"main"和这个,它们将并发运行。

循环告诉我们要执行这个10次:

fmt.Println(<-c)

这个调用将阻塞,直到我们从通道接收到一个整数并打印它。一旦出现10次我们就会发出信号,表示这个例程已经结束

quit <- 0

现在让我们回到主例程,当另一个例程启动时它调用了函数&;finbonacci",没有&;go"关键字,所以这个调用阻塞在这里

fibonacci(c, quit)

现在分析函数

x, y := 0, 1
for {
select {
case c <- x:
x, y = y, x+y
case <-quit:
fmt.Println("quit")
return
}
}

开始一个无限循环,在每次迭代中,我们将尝试从select

中执行一个case第一种情况将尝试无限期地将斐波那契数列的值发送到通道,在某些时候,一旦另一种例程到达fmt.Println(<-c)语句,这种情况将被执行,值将被重新计算,下一次循环迭代将发生。

case c <- x:
x, y = y, x+y 

第二种情况还不能运行,因为没有人发送任何东西到"quit"通道。

case <-quit: 
fmt.Println("quit")
return
}

经过10次迭代后,第一个例程将停止接收新值,因此第一个select case将无法运行

case c <- x: // this will block after 10 times, nobody is reading
x, y = y, x+y 

此时,没有"select"可执行时,主例程是"blocked"(从逻辑的角度来看,更像是暂停)。

最后,在某个时刻,第一个程序将使用"quit"发出它已经完成的信号。频道

quit <- 0

允许执行第二个"select"情况,这将打破无限循环,并允许"fibonacci"函数返回和主函数将能够以干净的方式完成。

希望这能帮助你理解斐波那契的例子。

现在,转到您的代码中,您没有等待匿名例程完成,事实上,它甚至没有完成。你的"b"方法正在发送"1,2,3"并立即返回。

由于它是main函数的最后一部分,因此程序终止。

如果你只看到"1,2"有时是因为"语句太慢,程序在打印3.

之前就终止了。

最新更新