为什么这里有通道的可变输出

我创建 5 个通道并发送数据 5 次:

package main

import "fmt"

func greet(c chan string) {

    fmt.Println("Hello " + <-c + "!")

}

func main() {

    fmt.Println("main() started")

    c := make(chan string)

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

        go greet(c)

    }

    c <- "AAA"

    c <- "BBB"

    c <- "CCC"

    c <- "DDD"

    c <- "EEE"

    fmt.Println("main() stopped")

}

我预计所有 5 个字符串都会被打印出来。但是,我发现输出可变。一些输出是:


$ ./rnchannel

main() started

Hello AAA!

Hello DDD!

Hello BBB!

Hello CCC!

Hello EEE!

main() stopped


$ ./rnchannel

main() started

Hello CCC!

Hello DDD!

main() stopped


$ ./rnchannel

main() started

Hello CCC!

Hello BBB!

Hello AAA!

Hello DDD!

main() stopped

为什么打印的行数可变?


慕莱坞森
浏览 82回答 2
2回答

繁花如伊

您不必等到打印完所有字符串才退出。一旦主线程到达执行结束,它就会关闭所有 goroutine 并结束程序。由于这种情况同时发生,因此无法确定将允许打印多少个字符串。

慕容3067478

当main()退出时,所有 goroutine 都会被杀死。无法保证您的其他 goroutine 会在此之前完成,这就是并发的本质。以下是修复方法。首先,让我们对 进行一些更改greet。让其休眠一会儿,以使问题更加明显。我们还将让它接受字符串而不是通道,稍后我们就会明白原因。func greet(str string) {    time.Sleep(100 * time.Millisecond)    fmt.Println("Hello " + str + "!")}我们不需要创建一堆 goroutine 从通道读取固定次数,而是需要一个 goroutine 从通道读取直到耗尽。使用 可以最简单地完成此操作range。这充分利用了渠道的优势。我们还需要一种方法来告诉主程序等待循环完成。使用第二个通道最容易做到这一点。更复杂的同步使用WaitGroups。c := make(chan string, 2)done := make(chan bool, 1)go func() {    for str := range(c) {        greet(str)    }    done <- true}()goroutine 会一直读取c直到关闭。然后它会发送true到频道done。两者都经过缓冲,以避免由于阻塞等待读取或写入通道而导致死锁。回到main,我们写入通道,显式关闭它,然后等待从 读取done。    c <- "AAA"    c <- "BBB"    c <- "CCC"    c <- "DDD"    c <- "EEE"    close(c)    <-done    fmt.Println("main() stopped")<-done将阻塞,直到有东西可读。这使得 goroutine 能够完成。并将它们整合在一起。package mainimport(    "fmt"    "time")func greet(str string) {    time.Sleep(100 * time.Millisecond)    fmt.Println("Hello " + str + "!")}func main() {    fmt.Println("main() started")    c := make(chan string, 2)    done := make(chan bool, 1)    go func() {        for str := range(c) {            greet(str)        }        done <- true    }()    c <- "AAA"    c <- "BBB"    c <- "CCC"    c <- "DDD"    c <- "EEE"    close(c)    <-done    fmt.Println("main() stopped")}
打开App,查看更多内容
随时随地看视频慕课网APP