主线程从不屈服于goroutine

时间:2015-05-21 18:25:20

标签: go

  • 编辑* - 取消注释两个运行时行并将Tick()更改为Sleep(),它按预期工作,每秒打印一个数字。保留代码,以便回答/评论有意义。

go go go.4.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()将打印每秒的任何计数,而主线程连续递增。我不是在寻找不同的代码来完成同样的事情。我的问题更多的是关于发生了什么以及我的期望在哪里错了?第二个结果特别没有任何意义,一次打印。据我所知,它们永远不应该打印得比一秒钟更近,对吗?

1 个答案:

答案 0 :(得分:6)

您的紧for循环中没有任何内容可以让Go调度程序运行。 只要有一个块或某些(全部?)函数调用,该调度就会考虑其他goroutine。

由于您最简单的解决方案是不添加对runtime.Gosched的调用。 E.g:

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