考虑以下来自the tour of go的示例。
如何确定从频道接收的顺序?
为什么x总是从gorouting中获得第一个输出?
听起来很合理,但是我没有找到任何有关它的文档。
我尝试添加一些睡眠,并且仍然从第一次执行的gorouting中获取输入。
c := make(chan int)
go sumSleep(s[:len(s)/2], c)
go sum(s[len(s)/2:], c)
x, y := <-c, <-c // receive from c
fmt.Println(x, y, x+y)
睡眠是,而是发送到通道之前。
最佳答案
为了给Adrian's answer添加一点:我们不知道这两个goroutine可能以什么顺序运行。如果您的睡眠在您的频道发送之前进入,并且睡眠了“足够长的时间”,1这将确保另一个goroutine可以运行到进行发送的地步。如果两个goroutine都“同时”运行,而没有等待(如原始的Tour示例中所示),则我们无法确定哪个goroutine首先真正到达其c <- sum
行。
在Go Playground上直接(或通过Tour网站)运行Tour的示例,我实际上得到:
-5 17 12
在输出窗口中,(因为我们知道
-9
在 slice 的第二个一半中)告诉我们第二个goroutine首先“到达那里”(到通道发送)。从某种意义上说,这只是运气,但是当使用Go Playground时,所有作业都在具有确定性的环境中运行,并且具有单个CPU和协作式调度,因此结果更加可预测。换句话说,如果第二个goroutine在一个运行中首先到达那里,那么它可能会在下一个运行中到达。如果运动场使用了多个CPU和/或不确定性较高的环境,则结果可能会从一次运行更改为下一次运行,但不能保证这一点。无论如何,假设您的代码执行了您所说的(并且我相信这样做),那么:
go sumSleep(s[:len(s)/2], c)
go sum(s[len(s)/2:], c)
让第一个发送者等待,第二个发送者首先运行。但这就是我们让两个例程竞赛时实际上已经观察到的情况。要查看更改,我们需要使第二个发送方延迟。
我在Go Playground here中制作了示例的修改版本,可打印更多注释。在第二个一半的总和中插入延迟后,我们将上半年的总和视为
x
:2nd half: sleeping for 1s
1st half: sleeping for 0s
1st half: sending 17
2nd half: sending -5
17 -5 12
正如我们所期望的那样,因为一秒钟足够长。
1“足够长”多长时间?好吧,这取决于:我们的计算机有多快?他们还在做什么其他事情?如果计算机足够快,那么延迟几毫秒甚至几纳秒就足够了。如果我们的计算机确实是一台很旧的计算机或非常忙于其他更高优先级的任务,那么几毫秒的时间可能不够用。如果问题足够大,则一秒钟的时间可能不够。如果您可以通过某种同步操作更好地控制时间,通常选择这样做是不明智的。例如,使用
sync.WaitGroup
变量允许您在自己的goroutine继续执行之前等待n个goroutine(对于某些运行时值n)调用Done
函数。游乐场代码,为方便起见复制到StackOverflow
package main
import (
"fmt"
"time"
)
func sum(s []int, c chan int, printme string, delay time.Duration) {
sum := 0
for _, v := range s {
sum += v
}
fmt.Printf("%s: sleeping for %v\n", printme, delay)
time.Sleep(delay)
fmt.Printf("%s: sending %d\n", printme, sum)
c <- sum
}
func main() {
s := []int{7, 2, 8, -9, 4, 0}
c := make(chan int)
go sum(s[:len(s)/2], c, "1st half", 0*time.Second)
go sum(s[len(s)/2:], c, "2nd half", 1*time.Second)
x, y := <-c, <-c
fmt.Println(x, y, x+y)
}
关于go - 怎样才能确定从 channel 接收的顺序呢?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/59789047/