带有通道的Goroutine的行为

时间:2019-01-28 14:10:09

标签: go channel goroutine channels

下面给出的代码输出有些混乱,请帮助我了解通道和goroutine的行为以及如何 执行实际上发生了?

我试图理解程序的流程,但是即使调用了goroutine,“ goroutine的调用”之后的语句也会执行, 稍后执行goroutines中的语句,

在第二次“调用goroutine”时,行为不同,并且打印/程序流的顺序发生了变化。

以下是代码:

py -3.5 -m pip install tensorflow==0.12.1

以上代码的结果:

    package main

    import "fmt"

    func main() {
        fmt.Println("1")
        done := make(chan string)
        go test(done)
        fmt.Println("7")
        fmt.Println(<-done)
        fmt.Println("8")
        fmt.Println(<-done)
        fmt.Println("9")
        fmt.Println(<-done)
    }
    func test(done chan string) {
        fmt.Println("2")
        done <- "3"
        done <- "10"
        fmt.Println("4")
        done <- "5"
       fmt.Println("6")
    }

请帮助我了解为什么以及如何得出此结果。

1 个答案:

答案 0 :(得分:0)

欢迎使用堆栈溢出。希望我能帮助您更好地了解频道和goroutines。

概念1:Channels

将通道可视化为一条管,数据在一端进入另一端。输入的第一个数据是从另一侧输出的第一个数据。有缓冲通道和非缓冲通道,但是对于您的示例,您只需要了解默认通道即可。无缓冲通道一次只允许一个值在通道中。

写到无缓冲通道

看起来像这样的代码将数据写入通道的一端。

ch <- value

现在,这段代码实际上等待执行,直到有人从通道中读取该值为止。一个无缓冲的通道一次只允许一个值位于其中,并且直到被读取后才继续执行。稍后我们将看到这如何影响代码执行的顺序。

从无缓冲通道读取

要从未缓冲的通道读取(可视化从通道中取出值),执行此操作的代码如下所示:

[value :=] <-ch
  

当您阅读代码文档时,方括号中的内容表示其中的内容是可选的。上面,没有[value:=],您只会从通道中取出一个值,并且不会将其用于任何用途。

现在,当通道中有值时,此代码有两个副作用。第一,它以我们现在所使用的例程从通道中读取值,然后继续该值。它的 other 效果是允许goroutine将该值放入通道以继续。这是理解示例程序所必需的关键点。

如果通道中尚无值,它将等待值写入通道,然后再继续。换句话说,线程阻塞直到通道具有要读取的值。

概念2:Goroutines

goroutine允许您的代码继续同时并行执行两段代码。这样可以使您的代码更快地执行,或同时遇到多个问题(例如,服务器上有多个用户同时从中加载页面)。

当您有多个并发执行的例程试图找出执行代码的顺序时,就会出现您的问题。这是一个很好的问题,其他人已经正确说明了这取决于。当您生成两个goroutine时,执行代码行的顺序是任意的。

下面带有goroutine的代码可能首先打印executing a()end main()。这是由于以下事实:产生gorouting意味着同时发生两个并发的执行流(线程)。在这种情况下,一个线程停留在main()中,另一个线程开始执行a()中的第一行。运行时如何决定选择先运行哪个是任意的。

func main() {
    fmt.Println("start main()")
    go a()
    fmt.Println("end main()")
}

func a() {
    fmt.Println("executing a()")
}

Goroutines +频道

现在,让我们使用一个通道来控制何时执行get的顺序。  现在唯一的区别是,我们创建一个通道,将其传递到goroutine中,并等待其值被写入,然后再继续执行main。从较早的时候开始,我们就讨论了如何从通道读取值的例程需要等到通道中有值才能继续。由于executing a()总是在写入通道之前打印,因此我们将始终等待读取放入通道中的值,直到executing a()已打印。由于我们在打印end main()之前从该通道读取(这是在写入通道之后发生的),因此executing a()将始终在end main()之前打印。我制作了this playground,因此您可以自己运行它。

func main() {
    fmt.Println("start main()")
    ch := make(chan int)
    go a(ch)
    <-ch
    fmt.Println("end main()")
}

func a(ch chan int) {
    fmt.Println("executing a()")
    ch <- 0
}

您的示例

我认为在这一点上您可以弄清楚什么时候会发生什么,以及可能以不同的顺序发生什么。我脑海中的第一次尝试是错误的(请参阅编辑历史记录)。你必须要小心!编辑后,我将无法给出正确的答案,因为我意识到这可能是一项家庭作业。

编辑:有关<-done

的更多语义

在我的第一次浏览时,我忘记提到fmt.Println(<-done)在概念上与以下内容相同。

value := <-done
fmt.Println(value)

这很重要,因为它可以帮助您看到main()线程从done通道读取时,它不会同时打印它。这是运行时的两个单独步骤。

我希望这会有所帮助。让我知道你是否有疑问。