为了账号安全,请及时绑定邮箱和手机立即绑定

如何确定从渠道接收的顺序?

如何确定从渠道接收的顺序?

Go
婷婷同学_ 2022-05-05 17:51:03
考虑以下go 之旅中的示例。如何确定频道的接收顺序?为什么 x 总是从 gorouting 获得第一个输出?这听起来很合理,但我没有找到任何关于它的文档。我尝试添加一些睡眠,但仍然 x 从第一次执行的 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)睡眠是在发送到通道之前。
查看完整描述

3 回答

?
猛跑小猪

TA贡献1858条经验 获得超8个赞

消息总是按照它们发送的顺序被接收。那是确定性的。

然而,跨并发 Goroutine 的任何给定操作的执行顺序是不确定的。因此,如果您有两个 goroutine 同时在一个通道上发送,您将无法知道哪个会先发送,哪个会再发送。如果您有两个 goroutine 在同一个频道上接收,则相同。


查看完整回答
反对 回复 2022-05-05
?
ITMISS

TA贡献1871条经验 获得超8个赞

我尝试添加一些睡眠,但仍然 x 从第一个执行的 goroutine 获取输入

除了@Adrian 写的内容之外,由于元组分配的语言规则,您的代码x将始终从 first recieveon获得结果c

任务分两个阶段进行。首先,左边的索引表达式和指针间接(包括选择器中的隐式指针间接)的操作数和右边的表达式都按通常的顺序计算。其次,作业是按从左到右的顺序进行的。


查看完整回答
反对 回复 2022-05-05
?
青春有我

TA贡献1784条经验 获得超8个赞

我们不知道这两个 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 1s

1st half: sleeping for 0s

1st half: sending 17

2nd half: sending -5

17 -5 12

正如我们所料,因为一秒钟“足够长”。


1 “足够长”有多长?好吧,这取决于:我们的计算机有多快?他们还做了多少其他事情?如果计算机足够快,几毫秒甚至几纳秒的延迟可能就足够了。如果我们的计算机非常旧或者非常忙于其他更高优先级的任务,那么几毫秒可能还不够。如果问题足够大,一秒钟可能不够用。如果您可以通过某种同步操作更好地控制它,那么选择某个特定的时间量通常是不明智的,通常您可以。例如,使用变量可以让您等待 n 个 goroutine(对于 n 的某个运行时值)调用sync.WaitGroupDone在你自己的 goroutine 继续之前运行。


Playground 代码,为方便起见,复制到 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)

}


查看完整回答
反对 回复 2022-05-05
  • 3 回答
  • 0 关注
  • 131 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信