【问题标题】:Main thread never yields to goroutine主线程永远不会屈服于 goroutine
【发布时间】:2015-08-03 13:59:14
【问题描述】:
  • edit * -- 取消注释两个运行时行并将 Tick() 更改为 Sleep() 并按预期工作,每秒打印一个数字。保持代码不变,这样 answer/cmets 才有意义。

go 版本 go1.4.2 darwin/amd64

当我运行以下命令时,我从未看到从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()

如果我取消注释运行时的东西,我会得到一些奇怪的结果,如下所示,一次打印(不是一秒钟):

> go run a.go
main
after Counter()
In Counter()
10062
36380
37351
38036
38643
39285
39859
40395
40904
43114

我期望go Counter() 将打印每秒的任何计数,同时它由主线程连续递增。我不是在寻找不同的代码来完成同样的事情。我的问题更多是关于发生了什么以及我的期望在哪里错了?特别是第二个结果没有任何意义,一次打印出来。据我所知,它们的打印间隔不应小于一秒,对吧?

【问题讨论】:

  • 我想你把time.Tick误认为time.Sleep了。
  • 原来这个更改修复了它,因此它可以按预期运行(使用 GOMAXPROCS=2)。
  • 是的,所有谜团都在 10 分钟内解开。万岁!

标签: go


【解决方案1】:

在您紧密的 for 循环中没有任何东西可以让 Go 调度程序运行。 每当一个阻塞或某些(哪个?全部?)函数调用时,调度会考虑其他 goroutine。

既然您都没有这样做,最简单的解决方案就是添加对runtime.Gosched 的调用。例如:

for {
    count++
    if count % someNum == 0 {
        runtime.Gosched()
    }
}

另请注意,在不锁定或同步的情况下从不同的 goroutine 写入和读取相同的变量是数据竞争,there are no benign data races,您的程序可以在读取正在写入的值时执行任何操作。 (而且同步也会让 Go 调度程序运行。)

对于一个简单的计数器,您可以使用atomic.AddInt64(&var, 1)atomic.LoadInt64(&var) 来避免锁定。

进一步说明(正如@tomasz 指出的,我完全错过了),time.Tick 不会延迟任何事情,您可能想要time.Sleepfor range time.Tick(…)

【讨论】:

  • 优秀的答案(再等6分钟接受不了)。
  • 使用 GOMAXPROCS=2 切换到 time.Sleep() 并按预期工作。我知道这段代码与其中的数据竞争一团糟,但我试图从更大的问题中提炼出问题,以便问你最终回答的问题。谢谢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-03-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多