Go协程如何完美地交错执行是如何实现的?

huangapple go评论122阅读模式
英文:

How is it possible that go routines interleave perfectly?

问题

我很惊讶地发现Go协程似乎完美地交错执行...看到这个之后,我开始相信有一些关于内部机制的信息我还没有学到。例如:

  1. $ go run x.go > output
  2. $ grep ping output | wc -l
  3. 404778
  4. $ grep pong output | wc -l
  5. 404777
  6. $ cat x.go
  7. package main
  8. import (
  9. "fmt"
  10. "time"
  11. )
  12. type Ball struct{ hits int }
  13. func main() {
  14. table := make(chan *Ball)
  15. go player("ping", table)
  16. go player("pong", table)
  17. table <- new(Ball) // game on; toss the ball
  18. time.Sleep(1 * time.Second)
  19. <-table // game over; grab the ball
  20. }
  21. func player(name string, table chan *Ball) {
  22. for {
  23. ball := <-table
  24. ball.hits++
  25. fmt.Println(name, ball.hits)
  26. //time.Sleep(1 * time.Millisecond)
  27. table <- ball
  28. }
  29. }

无论你在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:

  1. $ go run x.go &gt; output
  2. $ grep ping output | wc -l
  3. 404778
  4. $ grep pong output | wc -l
  5. 404777
  6. $ cat x.go
  7. package main
  8. import (
  9. &quot;fmt&quot;
  10. &quot;time&quot;
  11. )
  12. type Ball struct{ hits int }
  13. func main() {
  14. table := make(chan *Ball)
  15. go player(&quot;ping&quot;, table)
  16. go player(&quot;pong&quot;, table)
  17. table &lt;- new(Ball) // game on; toss the ball
  18. time.Sleep(1 * time.Second)
  19. &lt;-table // game over; grab the ball
  20. }
  21. func player(name string, table chan *Ball) {
  22. for {
  23. ball := &lt;-table
  24. ball.hits++
  25. fmt.Println(name, ball.hits)
  26. //time.Sleep(1 * time.Millisecond)
  27. table &lt;- ball
  28. }
  29. }

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 &lt;- ball) can only complete once someone somewhere has done a read (&lt;-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并运行...

  1. tmp export GOMAXPROCS=2
  2. tmp go run balls.go
  3. ping 1
  4. pong 2
  5. tmp go run balls.go
  6. pong 1
  7. ping 2
  8. 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 ...

  1. tmp export GOMAXPROCS=2
  2. tmp go run balls.go
  3. ping 1
  4. pong 2
  5. tmp go run balls.go
  6. pong 1
  7. ping 2
  8. tmp

As you can see above the goroutines order of execution is not deterministic

huangapple
  • 本文由 发表于 2015年3月16日 03:08:41
  • 转载请务必保留本文链接:https://go.coder-hub.com/29064946.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定