Может ли компилятор go изменить порядок следующего кода?

#go #concurrency #happens-before #data-race

#Вперед #параллелизм #происходит -перед #гонка данных

Вопрос:

Недавно я обнаружил, что некоторый код выглядит следующим образом:

 var m map[int]int

func writem() {
    tmpm := make(map[int]int)
    for i := 0; i < 4000000; i   {
        tmpm[i] = i   10
    }
    m = tmpm
}

func readm() {
    for k, v := range m {
        _, _ = k, v
    }
}

func main() {
    writem()
    go readm()
    writem()
}
  

Эта программа работает нормально, но я думаю, что writem тело функции может быть переупорядочено путем перемещения m = tmpm перед циклом for, потому что это не меняет поведение внутри этой подпрограммы. И это изменение порядка вызовет concurrent map read and map write проблему. Как гласит модель памяти Go:

компиляторы и процессоры могут изменить порядок операций чтения и записи, выполняемых в рамках одной подпрограммы, только в том случае, если изменение порядка не изменяет поведение в рамках этой подпрограммы, как определено спецификацией языка

Я прав, или писать подобный код безопасно?

Комментарии:

1. Приведенная вами аннотация не применима к вашей ситуации, поскольку операции чтения и записи не выполняются в одной и той же подпрограмме. Итак, я не совсем понимаю ваш вопрос.

Ответ №1:

Эта программа работает нормально.

Нет, это не так.


Результаты программы не определены. У вас гонка данных на карте m .

 $ go run -race racer.go
==================
WARNING: DATA RACE
Write at 0x000000510fa0 by main goroutine:
  main.writem()
      /home/peter/gopath/src/racer.go:10  0xa7
  main.main()
      /home/peter/gopath/src/racer.go:22  0x4c

Previous read at 0x000000510fa0 by goroutine 13:
  [failed to restore the stack]

Goroutine 13 (finished) created at:
  main.main()
      /home/peter/gopath/src/racer.go:21  0x47
==================
Found 1 data race(s)
exit status 66
$ 
  

racer.go :

 package main

var m map[int]int

func writem() {
    tmpm := make(map[int]int)
    for i := 0; i < 4000000; i   {
        tmpm[i] = i   10
    }
    m = tmpm
}

func readm() {
    for k, v := range m {
        _, _ = k, v
    }
}

func main() {
    writem()
    go readm()
    writem()
}
  

Игровая площадка: https://play.golang.org/p/OcWmK7ioMkD


Ссылка: Go: Детектор гонки данных