为什么我的for循环仅在我放慢速度时才起作用?

时间:2019-02-07 17:12:42

标签: go

此代码显示0,但是如果我插入time.Sleep(0)到更新程序中 循环,它显示> 1

var Nonce int = 0

func Updater(){
    for{
        Nonce += 1
    }
}

func main(){
    go Updater()
    time.Sleep(time.Second)
    fmt.Printf("%d\n",Nonce)
}

2 个答案:

答案 0 :(得分:2)

nonce.go

package main

import (
  "fmt"
  "time"
)

var Nonce int = 0

func Updater() {
  for {
      Nonce += 1
  }
}

func main() {
  go Updater()
  time.Sleep(time.Second)
  fmt.Printf("%d\n", Nonce)
}

首先,Go gc编译器是优化的编译器。 goroutine之间没有同步。因此,Nonce += 1语句被忽略,Nonce的值保持为零。查看编译后的代码:

$ go build nonce.go
$ objdump -d -S ./nonce

输出:

var Nonce int = 0

func Updater() {
    for {
        Nonce += 1
  4888f0:   eb fe                   jmp    4888f0 <main.Updater>
  4888f2:   cc                      int3   
    }
}

第二,如果我们运行Go数据竞争检测器,则会抑制某些优化。因此,检测到Nonce变量的数据争用条件。数据争用的结果是不确定的。

$ go run -race nonce.go
==================
WARNING: DATA RACE
Read at 0x0000005f2648 by main goroutine:
  main.main()
      /home/peter/gopath/src/nonce.go:19 +0x63

Previous write at 0x0000005f2648 by goroutine 6:
  main.Updater()
      /home/peter/gopath/src/nonce.go:12 +0x56

Goroutine 6 (running) created at:
  main.main()
      /home/peter/gopath/src/nonce.go:17 +0x46
==================
42758109
Found 1 data race(s)
exit status 66
$  

答案 1 :(得分:0)

您的main函数启动一个goroutine来运行Updater,然后立即退出。没有睡眠,Updater没有时间开始做自己的事情。这段代码是 racy -它有时会按您期望的那样工作,有时却不会。您必须以某种方式使Updatermain同步,例如使用通道或等待组。

此外,您将在一个goroutine中更新Nonce全局,而在另一个goroutine中读取它-这是一场数据竞赛。您需要使用互斥锁来同步对此var的访问。


这是代码的一种更正确的变体,尽管它仍然很荒谬(为什么您需要一个goroutine来运行忙循环?)

package main

import (
    "fmt"
    "sync"
)

var Nonce int = 0

func Updater(wg *sync.WaitGroup) {
    defer wg.Done()
    for i := 0; i < 1000; i++ {
        Nonce += 1
    }
}

func main() {
    var wg sync.WaitGroup
    wg.Add(1)
    go Updater(&wg)
    wg.Wait()
    fmt.Printf("%d\n", Nonce)
}

这里,我们对WaitGroup使用Updater来向主goroutine发出“我已完成”信号,然后该主goroutine才检查Nonce的值。该程序每次应打印“ 1000”