主线程从不让出给goroutine。

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

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.Sleepfor 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(…).

huangapple
  • 本文由 发表于 2015年5月22日 02:25:19
  • 转载请务必保留本文链接:https://go.coder-hub.com/30381558.html
匿名

发表评论

匿名网友

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

确定