Почему мой цикл for работает только тогда, когда я замедляю его? - PullRequest
0 голосов
/ 07 февраля 2019

Этот код печатает 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 голосов
/ 07 февраля 2019

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 является оптимизирующим компилятором.Нет синхронизации между горутинами.Следовательно, оператор 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
$  
0 голосов
/ 07 февраля 2019

Ваша функция main раскручивает программу, чтобы запустить Updater, а затем немедленно завершает работу.Без сна у Updater нет времени начинать и делать свое дело.Этот код racy - иногда он работает так, как вы ожидаете, а иногда нет.Вы должны каким-то образом синхронизировать Updater последовательность с main, используя, например, канал или группу ожидания.

Кроме того, вы обновляете глобал Nonce в одной процедуре и читаете ее в другой.- это гонка данных.Вам нужно будет синхронизировать доступ к этой переменной с мьютексом.


Вот более правильный вариант вашего кода, хотя он все еще довольно бессмысленный (зачем вам нужна программа для запуска занятыхloop?)

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, чтобы сигнализировать «Я закончил» главной программе, которая только затем проверяет значение Nonce.Эта программа должна печатать "1000" каждый раз

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...