英文:
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 := <-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
}
}
}
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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论