Go在无限循环中显示奇怪的行为

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

Go showing strange behavior in infinite loop

问题

我在我的Go代码中遇到了非常奇怪的行为。总体来说,当我有以下代码时:

for {
  if messagesRecieved == l {
    break
  }
  select {
  case result := <-results:
    newWords[result.index] = result.word
    messagesRecieved += 1
  default:
    // fmt.Printf("messagesRecieved: %v\n", messagesRecieved)
    if i != l {
      request := Request{word: words[i], index: i, thesaurus_word: results}
      requests <- request
      i += 1
    }
  }
}

程序会冻结并无法继续执行,但是当我取消注释 fmt.Printf 命令时,程序就可以正常工作。你可以在这里看到完整的代码。有人知道是什么原因导致这种行为吗?

英文:

I am having very strange behavior in my Go code. The overall gist is that when I have

for {
  if messagesRecieved == l {
    break
  }
  select {
  case result := &lt;-results:
    newWords[result.index] = result.word
    messagesRecieved += 1
  default:
    // fmt.Printf(&quot;messagesRecieved: %v\n&quot;, messagesRecieved)
    if i != l {
      request := Request{word: words[i], index: i, thesaurus_word: results}
      requests &lt;- request
      i += 1
    }
  }
}

the program freezes and fails to advance, but when I uncomment out the fmt.Printf command, then the program works fine. You can see the entire code here. does anyone know what's causing this behavior?

答案1

得分: 2

Go语言在1.1.2版本(当前版本)中仍然只有最初发布时的协作调度。编译器通过插入调度点来改进行为。根据内存模型推断,这些调度点位于通道操作旁边。此外,还有一些众所周知但故意未记录的地方,比如发生I/O的地方。这就解释了为什么取消注释fmt.Printf会改变程序的行为。顺便说一句,Go的最新版本现在支持抢占式调度。

你的代码让一个goroutine忙于执行默认的select case。由于没有其他调度点(假设默认的GOMAXPROCS=1)而没有打印输出,其他goroutine没有机会取得进展。

我建议以一种避免自旋(忙等待)的方式重写程序的逻辑。一种可能的方法是在默认case中使用通道发送操作。通过使用带缓冲的通道,还可以免费获得一个简单的限制器。

英文:

Go in version 1.1.2 (the current release) has still only the original (since initial release) cooperative scheduling of goroutines. The compiler improves the behavior by inserting scheduling points. Inferred from the memory model they are next to channel operations. Additionaly also in some well known, but intentionally undocumented places, such as where I/O occurs. The last explains why uncommenting fmt.Printf changes the behavior of your program. And, BTW, the Go tip version now sports a preemptive scheduler.

Your code keeps one of your goroutines busy going through the default select case. As there are no other scheduling points w/o the print, no other goroutine has a chance to make progress (assuming default GOMAXPROCS=1).

I recommend to rewrite the logic of the program in a way which avoids spinning (busy waiting). One possible approach is to use a channel send in the default case. As a perhaps nice side effect of using a buffered channel for that, one gets a simple limiter from that for free.

huangapple
  • 本文由 发表于 2013年9月9日 15:01:43
  • 转载请务必保留本文链接:https://go.coder-hub.com/18692998.html
匿名

发表评论

匿名网友

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

确定