青春有我
我们不知道这两个 goroutine 可能以什么顺序运行。如果您的睡眠发生在您的通道发送之前,并且睡眠“足够长”,则1这将保证另一个goroutine 可以运行到进行发送的点。如果两个 goroutine “同时”运行并且都不等待(如原始 Tour 示例中那样),我们无法确定哪个 goroutine 将c <- sum首先到达它的行。在 Go Playground 上运行 Tour 的示例(直接或通过 Tour 网站),我实际上得到:-5 17 12在输出窗口中,其中(因为我们知道它-9在切片的第二半)告诉我们第二个goroutine 首先“到达”(到通道发送)。从某种意义上说,这只是运气——但是当使用 Go Playground 时,所有作业都在一个相当确定的环境中运行,使用单个 CPU 并使用协作调度,因此结果更可预测。换句话说,如果第二个 goroutine 在一次运行中首先到达那里,它可能会在下一次运行中到达那里。如果 Playground 使用多个 CPU 和/或不确定性较低的环境,则结果可能会从一次运行到下一次发生变化,但不能保证这一点。无论如何,假设您的代码执行您所说的(我相信它确实如此),这:go sumSleep(s[:len(s)/2], c)go sum(s[len(s)/2:], c)让第一个发件人等待,第二个发件人先运行。但这就是我们已经观察到的当我们让这两个例程比赛时实际发生的情况。要查看更改,我们需要使第二个发件人延迟。我在这里的 Go Playground 中制作了示例的修改版本,打印了更多注释。在第二半和中插入延迟后,我们将前半和视为x:2nd half: sleeping for 1s1st half: sleeping for 0s1st half: sending 172nd half: sending -517 -5 12正如我们所料,因为一秒钟“足够长”。1 “足够长”有多长?好吧,这取决于:我们的计算机有多快?他们还做了多少其他事情?如果计算机足够快,几毫秒甚至几纳秒的延迟可能就足够了。如果我们的计算机非常旧或者非常忙于其他更高优先级的任务,那么几毫秒可能还不够。如果问题足够大,一秒钟可能不够用。如果您可以通过某种同步操作更好地控制它,那么选择某个特定的时间量通常是不明智的,通常您可以。例如,使用变量可以让您等待 n 个 goroutine(对于 n 的某个运行时值)调用sync.WaitGroupDone在你自己的 goroutine 继续之前运行。Playground 代码,为方便起见,复制到 StackOverflowpackage mainimport ( "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)}