去教程选择语句

Posted

技术标签:

【中文标题】去教程选择语句【英文标题】:go tutorial select statement 【发布时间】:2016-04-28 03:06:49 【问题描述】:

我正在处理 tour.golang.org 上的示例,但遇到了我不太理解的代码:

package main
import "fmt"

func fibonacci(c, quit chan int) 
    x, y := 0, 1
    for 
        select 
        case c <- x: // case: send x to channel c?
            x, y = y, x+y
        case <-quit: // case: receive from channel quit?
            fmt.Println("quit")
            return
        
    


func main() 
    c := make(chan int)
    quit := make(chan int)
    go func()  // when does this get called?
        for i := 0; i < 10; i++ 
            fmt.Println(<-c)
        
        quit <- 0
    ()
    fibonacci(c, quit)

我了解渠道如何工作的基础知识,但我不明白上述 select 语句的工作原理。教程上的解释说:

“select 语句让 goroutine 等待多个通信操作。 一个 select 阻塞,直到它的一个 case 可以运行,然后它执行那个 case。如果多个都准备好了,它会随机选择一个。”

但是案件是如何被执行的呢?据我所知,他们在说:

案例:将 x 发送到通道 c

案例:从退出接收

我想我明白第二个只有在 quit 有值时才会执行,这稍后会在 go func() 中完成。但是第一个案例检查的是什么?另外,在 go func() 内部,我们显然是在打印 c 的值,但是 c 中不应该有任何东西吗?我能想到的唯一解释是 go func() 在调用 fibonacci() 之后以某种方式执行。我猜这是一个我也不完全理解的 goroutine,它看起来就像魔法一样。

如果有人可以通过此代码并告诉我它在做什么,我将不胜感激。

【问题讨论】:

您关于“go func() // 何时调用此函数的问题?” --- 它在那里被调用 - 注意最后的 ()。这是一个内联函数/闭包,“go”异步运行它。两个线程互相交谈。它在后台运行 - 它从 c 读取(斐波那契正在写入的),一旦它读取了 10 个项目,它就会写入退出 - 告诉斐波那契它已经完成并且可以退出。内联块等待 c 上的数据。然后通过调用完成工作的 fibonacci() 继续执行。您可能会阅读本文并浏览其他文章。 blog.golang.org/pipelines 【参考方案1】:

你已经明白了。

在 go func() 中,我们显然是从 c 中打印值,但 c 中不应该有任何东西吗?我能想到的唯一解释是 go func() 在调用 fibonacci() 之后以某种方式执行。我猜这是一个 goroutine

是的,go 关键字启动了一个 goroutine,所以 func() 将与 fibonacci(c, quit) 同时运行。 Println 中通道的接收只是阻塞,直到有东西要接收

【讨论】:

【参考方案2】:

记住通道会阻塞,所以选择语句如下:

select 
case c <- x: // if I can send to c
    // update my variables
    x, y = y, x+y
case <-quit: // If I can receive from quit then I'm supposed to exit
    fmt.Println("quit")
    return

没有default 案例意味着“如果我不能发送到 c 并且我不能从退出中读取,则阻止直到我可以。”

然后在您的主进程中,您分拆另一个从 c 读取的函数以打印结果

for i:=0; i<10; i++ 
    fmt.Println(<-c)  // read in from c

quit <- 0  // send to quit to kill the main process.

这里的关键是要记住通道阻塞,并且您正在使用两个无缓冲通道。使用go 衍生出第二个功能让您可以从c 消费,因此fibonacci 将继续。


Goroutines 是所谓的“绿色线程”。使用关键字go 启动函数调用会将其转为独立于执行主线运行的新进程。本质上,main()go func() ... 是同时运行的!这很重要,因为我们在这段代码中使用了生产者/消费者模式。

fibonacci 生成值并将它们发送到c,而从 main 生成的匿名 goroutine 使用来自 c 的值并处理它们(在这种情况下,“处理它们”只是意味着打印到屏幕上)。我们不能简单地生成所有值然后使用它们,因为c 会阻塞。此外,fibonacci 将永远产生更多的值(或者直到整数溢出),所以即使你有一个具有无限长缓冲区的魔法通道,它也永远不会到达消费者。

【讨论】:

好的,我想我理解了 switch 语句,但你能解释一下你的意思吗:“使用 go 衍生出第二个函数让你从 c 消费,所以斐波那契将继续。” @Touchdown 我重新阅读了这个问题,并注意到您对 goroutines 的困惑。我编辑了一个小解释 谢谢,你的解释比教程上的清楚多了!【参考方案3】:

理解这个代码示例有两个关键点:

首先,让我们回顾一下无缓冲通道的工作原理。来自documentation

如果通道没有缓冲,发送方会阻塞,直到接收方有 收到了价值。

请注意,代码示例中的两个通道 cquit 均未缓冲。

其次,当我们使用go关键字启动一个新的goroutine时,执行将与其他例程并行发生。所以在示例中,我们运行了两个 go 例程:由func main() 启动的例程,以及在func main() 内部由go func()... 启动的例程。

我在这里添加了一些内联 cmets,这应该会让事情更清楚: 包主 导入“fmt”

func fibonacci(c, quit chan int) 
    x, y := 0, 1
    for  // this is equivalent to a while loop, without a stop condition
        select 
        case c <- x: // when we can send to channel c, and because c is unbuffered, we can only send to channel c when someone tries to receive from it
            x, y = y, x+y
        case <-quit: // when we can receive from channel quit, and because quit is unbuffered, we can only receive from channel quit when someone tries to send to it
            fmt.Println("quit")
            return
        
    


func main() 
    c := make(chan int)
    quit := make(chan int)
    go func()  // this runs in another goroutine, separate from the main goroutine
        for i := 0; i < 10; i++ 
            fmt.Println(<-c)
        
        quit <- 0
    ()
    fibonacci(c, quit) // this doesn't start with the go keyword, so it will run on the go routine started by func main()

【讨论】:

以上是关于去教程选择语句的主要内容,如果未能解决你的问题,请参考以下文章

Java入门到架构师教程之JavaScript流程控制语句

Unity游戏开发C#基础分支控制语句

Unity游戏开发C#基础循环控制语句

C++| C++ 入门教程 程序流程结构

sql语句去重

Java入门教程三(流程控制)