为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

Go
莫回无 2022-08-01 18:05:36
我一直在关注go tour的例子,我不明白这是如何运作的 https://tour.golang.org/concurrency/5package mainimport "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 mainimport "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,为什么?
查看完整描述

2 回答

?
波斯汪

TA贡献1811条经验 获得超4个赞

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


首先是匿名函数


go func() {

    for i := 0; i < 10; i++ {

        fmt.Println(<-c)

    }

    quit <- 0

}()

“go”关键字将启动一个新的 goroutine,所以现在我们有了 “main” goroutine,而这个,它们将同时运行。


循环告诉我们,我们将执行此命令 10 次:


fmt.Println(<-c)

此调用将阻塞,直到我们从通道接收到整数并打印它。一旦这种情况发生10次,我们将发出信号,表明这个goroutine已经完成了。


quit <- 0

现在让我们回到主 goroutine,当另一个 goroutine 启动时,它调用了函数 “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

    }

}

我们开始一个无限循环,在每次迭代中,我们将尝试从选择中执行一个案例


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


case c <- x:

        x, y = y, x+y 

第二种情况还不能运行,因为没有人向“退出”通道发送任何内容。


case <-quit: 

        fmt.Println("quit")

        return

}

经过 10 次迭代后,第一个 goroutine 将停止接收新值,因此第一个选择事例将无法运行


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

        x, y = y, x+y 

此时,“选择”中没有一个案例可以执行,主goroutine被“阻止”(从逻辑角度来看更像是暂停)。


最后,在某个时候,第一个goroutine将发出信号,表明它已经完成了使用“退出”通道


quit <- 0

这允许执行第二个“选择”情况,这将打破无限循环并允许“斐波那契”函数返回,主函数将能够以干净的方式完成。


希望这有助于您理解斐波那契的例子。


现在,移动到你的代码,你不是在等待匿名的goroutine完成,事实上,它甚至没有完成。您的“b”方法正在发送“1,2,3”并立即返回。


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


如果你只看到“1,2”有时是因为“fmt”。Println“语句太慢,程序在可以打印 3 之前终止。


查看完整回答
反对 回复 2022-08-01
?
天涯尽头无女友

TA贡献1831条经验 获得超9个赞

首先,在 中,语句尝试选择以下两个操作中的第一个完成操作:func fibonacciselect

  1. c <- x

  2. <- quit

它相当容易理解,它试图从一个名为的通道接收值(并忽略接收的值)。<- quitquit

c <- x表示发送等于 x 的值(是 x 的副本)。这似乎是解锁,但在Go中,当没有接收器时,通过无缓冲通道发送(在Go tour中进行了解释)阻止。

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

因此,每当评估时,此语句都会取消阻止(完成)。也就是说,循环的每次迭代。<-c

对于您的代码,虽然所有值 , 、 保证通过通道发送(并接收),但返回并因此保留而不保证完成。123func bfunc mainfmt.Println(3)

在Go中,当返回时,程序终止,未完成的goroutine没有机会完成其工作 - 因此有时它会打印并停止工作。func main3


查看完整回答
反对 回复 2022-08-01
  • 2 回答
  • 0 关注
  • 90 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号