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

问题描述 投票:-2回答:3

编辑:

感谢您的回答。我想我没有清楚地写出这个问题。问题是:可以确定x或y(在“ go的例子”中)是否为切片的前半部分。据我所知,答案是否定的。

此外,我是这个社区的新手,我想了解为什么人们不赞成这个问题。谢谢


请考虑以下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)

睡眠是之前发送到频道。

谢谢,

go channel goroutine
3个回答
0
投票

Adrian's answer添加一点:我们不知道两个goroutine可能以什么顺序运行。如果您的睡眠是在频道发送之前进入的,并且睡眠“足够长的时间”,则1将确保other goroutine可以运行到发送它的地步。如果两个goroutine都“同时”运行,并且都没有等待(如原始的Tour示例中所示),则我们无法确定哪个goroutine首先真正到达其c <- sum行。

[在Go Playground上直接或通过Tour网站运行Tour的示例,我实际上得到了:

-5 17 12

在输出窗口中,(由于我们知道-9在切片的第二个一半中),所以告诉我们second goroutine首先“到达那里”(到通道发送)。从某种意义上说,这只是运气,但是当使用Go Playground时,所有作业都在具有确定性的环境中运行,具有单个CPU和协作式调度,因此结果更加可预测。换句话说,如果第二个goroutine在一个运行中首先到达那里,那么它可能会在下一个运行中到达。如果操场上使用了多个CPU和/或不确定性较高的环境,则结果可能会从一次运行更改为下一次运行,但不能保证这一点。

无论如何,假设您的代码执行了您所说的(并且我相信它会这样做,这:

go sumSleep(s[:len(s)/2], c)
go sum(s[len(s)/2:], c)

有第一个发送者等待,第二个发送者首先运行。但这就是我们让两个例程竞赛时实际上已经观察到的情况。要查看change,我们需要使second发送方延迟。

我在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

“足够长”是多少时间?好吧,这取决于:我们的计算机有多快?他们还在做什么其他事情?如果计算机足够快,那么延迟几毫秒甚至几纳秒就足够了。如果我们的计算机确实是一台很旧的计算机或非常忙于其他更高优先级的任务,那么几毫秒的时间可能不够用。如果问题足够大,则一秒钟的时间可能不够。如果您可以通过某种synchronization操作更好地控制它,通常选择一个特定的time通常是不明智的。例如,使用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) }

总是

总是

接收消息按它们的发送顺序。这是确定性的。
但是,在并发Goroutine中执行任何给定操作的顺序是确定性的。因此,如果您有两个goroutine在一个通道上同时发送,您将不知道哪个先发送,第二个发送。如果您有两个goroutine在同一频道上接收,则相同。

我试图增加一些睡眠,并且仍然从第一个执行的goroutine中获取输入信息
除了@Adrian所写的内容之外,由于元组分配的语言规则,在您的代码中x总是从recieve的第一个c得到结果

作业分两个阶段进行。首先,左侧的索引表达式和指针间接操作(包括选择器中的隐式指针间接操作)和右侧的表达式的操作数均按通常的顺序求值。其次,分配是从左到右执行的。


6
投票
总是

总是


1
投票
我试图增加一些睡眠,并且仍然从第一个执行的goroutine中获取输入信息
© www.soinside.com 2019 - 2024. All rights reserved.