戈朗通道输出顺序


func main() {
messages := make(chan string)
go func() { messages <- "hello" }()
go func() { messages <- "ping" }()
msg := <-messages
msg2 := <-messages
fmt.Println(msg)
fmt.Println(msg2)

上面的代码在我的终端上始终打印"ping",然后打印"hello"。 我对打印的顺序感到困惑,所以我想知道我是否可以澄清我的想法。

我知道无缓冲的通道在等待发送方和接收方时都会阻塞。因此,在上述情况下,当执行这两个 go 例程时,在这两种情况下都没有接收器。所以我猜测这两个例程都会阻塞,直到通道上有接收器可用。

现在。。。我假设第一个"你好"被尝试进入频道,但必须等待......同时,"ping"尝试,但再次必须等待。然后

msg := <- messages

出现,所以我假设在那个阶段,程序将任意选择一个等待的 goroutine,并允许它将其消息发送到通道中,因为 msg 已准备好接收。

但是,似乎无论我运行程序多少次,总是分配有"ping"的msg和分配"hello"的msg2,这给人的印象是"ping"总是优先发送(到msg(。为什么?

这不是关于读取通道的顺序,而是关于 goroutines 执行的顺序,这是不保证的。

尝试从您写入通道的函数(写入之前和之后("Println",我认为它应该与从通道读取的顺序相同。

在 Golang 规范通道中,顺序描述为:-

通道充当先进先出队列。例如,如果一个 goroutine 在一个通道上发送值,第二个 goroutine 接收 它们,值按发送顺序接收。

它将打印哪个值首先可用,以便在另一端接收。 如果要同步它们,请使用不同的频道或添加wait Groups.

package main
import (
"fmt"
)
func main() {
messages1 := make(chan string)
messages2 := make(chan string)
go func(<-chan string) {
messages2 <- "ping" 
}(messages2)
go func(<-chan string) {
messages1 <- "hello" 
}(messages1)
fmt.Println(<-messages1)
fmt.Println(<-messages2)
}

如果您看到,您可以使用不同的渠道根据自己的选择轻松获得所需的任何价值。

去游乐场

我刚刚经历了同样的事情。 在这里查看我的帖子: Golang 频道,执行顺序

像你一样,我看到了一种违反直觉的模式。在一个实际上不应该有模式的地方。 一旦你启动了一个go进程,你就启动了一个执行线程,基本上所有的赌注在那个时候都是关于线程执行其步骤的顺序。 但是,如果要有一个订单,逻辑告诉我们第一个被调用的订单将首先被执行。

实际上,如果每次都重新编译该程序,结果会有所不同。这就是我开始在本地计算机上编译/运行它时发现的。为了使结果随机,我不得不"弄脏"文件,例如添加和删除空格。然后编译器会重新编译程序,然后我会得到一个随机的执行顺序。但是在 go 沙盒中编译时,结果总是相同的。

使用沙盒时,结果显然已缓存。 我无法使用无关紧要的更改在沙盒中获取更改顺序。我让它改变的唯一方法是发布时间。在启动 go 语句之间的 sleep(1( 命令。然后,第一个启动的将是每次执行的第一个。我仍然不认为我会把我的生命押在继续发生这件事上,因为它们是独立的执行线程,没有保证。

底线是,我看到了一个确定性的结果,不应该有确定性。 这就是让我陷入困境的原因。 当我发现结果在正常环境中确实是随机的时,我完全清楚了。沙盒是一个很好的工具。但这不是一个正常的环境。在本地编译并运行代码,您将看到预期的不同结果。

当我第一次遇到这个时,我很困惑。 但现在我很清楚这一点。 原因不是关于渠道,而是关于GoRoutine。

正如 Go Memory Model 所提到的,不能保证 goroutine 的运行和退出,所以当你创建两个 goroutine 时,你不能确保它们按顺序运行。

因此,如果您希望打印遵循FIFO规则,则可以像这样更改代码:

func main() {
messages := make(chan string)
go func() {
messages <- "hello"
messages <- "ping"
}()
//go func() { messages <- "ping" }()
msg := <-messages
msg2 := <-messages
fmt.Println(msg)
fmt.Println(msg2)
}

最新更新