Почему код в цикле не выполняется, когда у меня есть две подпрограммы - PullRequest
1 голос
/ 20 июня 2019

Я столкнулся с проблемой в Голанге

var a = 0
func main() {
        go func() {
                for {
                        a = a + 1
                }
        }()
        time.Sleep(time.Second)
        fmt.Printf("result=%d\n", a)
}
  • Ожидается: результат = (большое целое число)
  • результат: результат = 0

Ответы [ 3 ]

2 голосов
/ 20 июня 2019

У вас есть состояние гонки, запустите вашу программу с флагом -race

go run -race main.go
==================
WARNING: DATA RACE
Read at 0x0000005e9600 by main goroutine:
  main.main()
      /home/jack/Project/GoProject/src/gitlab.com/hooshyar/GoNetworkLab/StackOVerflow/race/main.go:17 +0x6c

Previous write at 0x0000005e9600 by goroutine 6:
  main.main.func1()
      /home/jack/Project/GoProject/src/gitlab.com/hooshyar/GoNetworkLab/StackOVerflow/race/main.go:13 +0x56

Goroutine 6 (running) created at:
  main.main()
      /home/jack/Project/GoProject/src/gitlab.com/hooshyar/GoNetworkLab/StackOVerflow/race/main.go:11 +0x46
==================
result=119657339
Found 1 data race(s)
exit status 66

что такое решение?
Есть какое-то решение, решение использует мьютекс:

var a = 0
func main() {
    var mu sync.Mutex

    go func() {
        for {
            mu.Lock()
            a = a + 1
            mu.Unlock()
        }
    }()
    time.Sleep(3*time.Second)
    mu.Lock()
    fmt.Printf("result=%d\n", a)
    mu.Unlock()
}

перед чтением и записью заблокируйте мьютекс, а затем разблокируйте его, теперь у вас нет расы, и в конце перезапуск будет большим.
Для получения дополнительной информации прочитайте эту тему.
Данные гонок в Го (Голанг) и как их исправить и это

Параллельность Голанга - гонки данных

0 голосов
/ 21 июня 2019

Как уже упоминали другие авторы, у вас есть гонка данных, но если вы сравниваете это поведение, например, с программой, написанной на C с использованием pthreads, вы упускаете некоторые важные данные.Ваша проблема не только во времени, но и в самом определении языка.Поскольку примитивы параллелизма запекаются в самом языке, модель памяти языка Go (https://golang.org/ref/mem) точно описывает, когда и как происходят изменения в одной подпрограмме - представьте, что подпрограммы являются «сверхлегкими потоками пространства пользователя», и вы выиграли »не слишком далеко - гарантированно будет виден коду, выполняющемуся в другой программе.

Без каких-либо синхронизирующих действий, таких как отправка / получение канала или синхронизация. Блокировка / разблокировка Mutex, модель памяти Go говорит, что любойизменения, которые вы вносите в 'a' внутри этой процедуры, не когда-либо должны быть видны основной программе. И, поскольку компилятор знает это, он может оптимизировать практически все, что есть в вашем цикле forИли нет.

Это похоже на ситуацию, когда у вас, скажем, локальная переменная int в C установлена ​​в 1, и, возможно, у вас есть цикл while, читающий эту переменную в цикле, ожидающий ее установкиISR до 0, но тогда ваш компилятор становится слишком умным и решает оптимизировать тест на ноль, потому что он thinks ваша переменная не может измениться внутри цикла, и вы действительно хотели бесконечный цикл, и поэтому вы должны объявить переменную как volatile, чтобы исправить «ошибку».

Если выбудем работать в Go, (мой текущий любимый язык, FWIW), потратить время на прочтение и тщательно освоить модель памяти Go, о которой говорилось выше, и она действительно окупится в будущем.

0 голосов
/ 20 июня 2019

Ваша программа работает в состоянии гонки. go может обнаруживать такие сценарии.

Попробуйте запустить вашу программу, используя go run -race main.go, предполагая, что ваше имя файла main.go. Это покажет, как произошла гонка, попытался написать внутри горутина, одновременное чтение по главной программе. Также будет напечатано случайное число типа int, как и ожидалось.

...