英文:
Main thread never yields to goroutine
问题
当我运行上述代码时,从go Counter()
中没有看到任何打印输出。
如果我取消注释运行时的代码,我会得到一些奇怪的结果,比如以下所有内容一次性打印出来(不是相隔一秒钟):
我期望的是,go Counter()
将在每秒钟打印出当前的计数,同时主线程不断递增计数。我不是很关心如何用不同的代码实现相同的功能。我的问题更多是关于发生了什么以及我的期望是错在哪里?特别是第二个结果没有任何意义,它们一次性打印出来。据我所知,它们应该不会在一秒钟内打印得如此接近,对吗?
英文:
- edit * -- uncomment the two runtime lines and change Tick() to Sleep() and it works as expected, printing one number every second. Leaving code as is so answer/comments make sense.
go version go1.4.2 darwin/amd64
When I run the following, I never see anything printed from go Counter()
.
package main
import (
"fmt"
"time"
//"runtime"
)
var count int64 = 0
func main() {
//runtime.GOMAXPROCS(2)
fmt.Println("main")
go Counter()
fmt.Println("after Counter()")
for {
count++
}
}
func Counter() {
fmt.Println("In Counter()")
for {
fmt.Println(count)
time.Tick(time.Second)
}
}
> go run a.go
main
after Counter()
If I uncomment the runtime stuff, I will get some strange results like the following all printed at once (not a second apart):
> go run a.go
main
after Counter()
In Counter()
10062
36380
37351
38036
38643
39285
39859
40395
40904
43114
What I expect is that go Counter()
will print whatever count is at every second while it is incremented continuously by the main thread. I'm not so much looking for different code to get the same thing done. My question is more about what is going on and where am I wrong in my expectations? The second results in particular don't make any sense, being printed all at once. They should never be printed closer than a second apart as far as I can tell, right?
答案1
得分: 6
你的紧密的for
循环中没有任何内容让Go调度器运行。
调度器在某个goroutine阻塞或某些(哪些?全部?)函数调用时会考虑其他的goroutine。
由于你没有做任何上述操作,最简单的解决方案是在循环中添加一个调用runtime.Gosched
。例如:
for {
count++
if count % someNum == 0 {
runtime.Gosched()
}
}
另外请注意,如果在不使用锁或同步的情况下从不同的goroutine中读写同一个变量,会产生数据竞争(data race),而没有良性的数据竞争,当读取正在被写入的值时,你的程序可能会做出任何行为。(而且同步也会让Go调度器运行。)
对于一个简单的计数器,你可以使用atomic.AddInt64(&var, 1)
和atomic.LoadInt64(&var)
来避免使用锁。
此外(正如@tomasz指出的,并且我完全忽略了),time.Tick
不会延迟任何操作,你可能想要使用time.Sleep
或for range time.Tick(…)
。
英文:
There is nothing in your tight for
loop that lets the Go scheduler run.
The schedule considers other goroutines whenever one blocks or on some (which? all?) function calls.
Since you do neither the easiest solution is to add a call to runtime.Gosched
. E.g.:
for {
count++
if count % someNum == 0 {
runtime.Gosched()
}
}
Also note that writing and reading the same variable from different goroutines without locking or synchronization is a data race and there are no benign data races, your program could do anything when reading a value as it's being written. (And synchronization would also let the Go scheduler run.)
For a simple counter you can avoid locks by using atomic.AddInt64(&var, 1)
and atomic.LoadInt64(&var)
.
Further note (as pointed out by @tomasz and completely missed by me), time.Tick
doesn't delay anything, you may have wanted time.Sleep
or for range time.Tick(…)
.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论