为什么这里有通道变量输出

Why variable output here with channels

我正在尝试从 here 修改的代码。我创建了 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

为什么打印的行数可变?

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

。当 main() 退出时,所有 goroutines 都会被杀死。不能保证你的其他 goroutines 会在那之前完成,这就是并发的本质。修复方法如下。

首先,让我们对 greet 进行一些更改。让它睡一会儿,使问题更加明显。我们还将让它接受一个字符串而不是通道,我们稍后会看到原因。

func greet(str string) {
    time.Sleep(100 * time.Millisecond)
    fmt.Println("Hello " + str + "!")
}

我们不需要创建一堆 goroutine 来从通道读取固定次数,而是需要一个 goroutine 从通道读取直到它用完。使用 range 最容易做到这一点。这充分利用了渠道。

我们还需要一种方法来告诉主程序等待循环完成。 This is easiest done with a second channel. More complex synchronization uses 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 main
import(
    "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")
}