英文:
How is it possible that go routines interleave perfectly?
问题
我很惊讶地发现Go协程似乎完美地交错执行...看到这个之后,我开始相信有一些关于内部机制的信息我还没有学到。例如:
$ go run x.go > output
$ grep ping output | wc -l
404778
$ grep pong output | wc -l
404777
$ cat x.go
package main
import (
"fmt"
"time"
)
type Ball struct{ hits int }
func main() {
table := make(chan *Ball)
go player("ping", table)
go player("pong", table)
table <- new(Ball) // game on; toss the ball
time.Sleep(1 * time.Second)
<-table // game over; grab the ball
}
func player(name string, table chan *Ball) {
for {
ball := <-table
ball.hits++
fmt.Println(name, ball.hits)
//time.Sleep(1 * time.Millisecond)
table <- ball
}
}
无论你在player函数中设置超时时间(或者完全删除它),你总是会得到 #ping == #ping +/- 1。
英文:
I'm surprised that go routines seem to interleave perfectly... After seeing this, I am starting to believe there's some missing information about the internals that I haven't learned about yet. Example:
$ go run x.go > output
$ grep ping output | wc -l
404778
$ grep pong output | wc -l
404777
$ cat x.go
package main
import (
"fmt"
"time"
)
type Ball struct{ hits int }
func main() {
table := make(chan *Ball)
go player("ping", table)
go player("pong", table)
table <- new(Ball) // game on; toss the ball
time.Sleep(1 * time.Second)
<-table // game over; grab the ball
}
func player(name string, table chan *Ball) {
for {
ball := <-table
ball.hits++
fmt.Println(name, ball.hits)
//time.Sleep(1 * time.Millisecond)
table <- ball
}
}
However long you set the timeout in the player function (or remove it all together) you always get #ping == #ping +/- 1.
答案1
得分: 7
你正在使用一个无缓冲通道,并且你的两个 goroutine 通过它进行同步。使用无缓冲通道,通道的写入操作(table <- ball
)只有在某个地方进行了读取操作(<-table
)后才能完成,因此单个 goroutine 永远无法读取它自己写入的值。这就是这个示例的整个目的。
英文:
You're using a un-buffered channel and your two goroutines synchronize with it. With an un-buffered channel the channel write (table <- ball
) can only complete once someone somewhere has done a read (<-table
) so a single goroutine can never read the value it's writing. That's the whole point of this example.
答案2
得分: 1
根据这篇博客的说法:
对于任意数量的玩家,Goroutines会完美地交错执行:
答案是因为Go运行时维护了一个等待接收者的FIFO队列(准备在特定通道上接收的goroutines),而在我们的情况下,每个玩家在将球传递到桌子上后立即准备好了。
英文:
According to this blog:
Goroutines would interleave perfectly for any number of players:
> The answer is because Go runtime holds waiting FIFO queue for receivers (goroutines ready to receive on the particular channel), and
> in our case every player gets ready just after he passed the ball on
> the table
答案3
得分: -5
默认情况下,GOMAXPROCS被设置为1,因此您会看到这种行为。如果您增加GOMAXPROCS,它将不再是确定性的。
参见这个答案的示例
编辑@DaveC不同意,但简单的测试结果显示不同。通道是同步的,但是goroutine的执行顺序不是确定的。这些是不同的概念。在上面的代码中输入,设置GOMAXPROCS > 1并运行...
➜ tmp export GOMAXPROCS=2
➜ tmp go run balls.go
ping 1
pong 2
➜ tmp go run balls.go
pong 1
ping 2
➜ tmp
如上所示,goroutine的执行顺序是不确定的。
英文:
By default GOMAXPROCS is set to 1 and as a result you see this behaviour. If you increase GOMAXPROCS it will no longer be deterministic.
See this answer for an example
EDIT @DaveC disagrees however a simple test shows otherwise. The channels are synchonised yes, however the order of the goroutines executing is not. These are different concepts. Type in the above code, set GOMAXPROCS > 1 and run ...
➜ tmp export GOMAXPROCS=2
➜ tmp go run balls.go
ping 1
pong 2
➜ tmp go run balls.go
pong 1
ping 2
➜ tmp
As you can see above the goroutines order of execution is not deterministic
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论