Может ли компилятор go изменить порядок следования кода? - PullRequest
0 голосов
/ 09 марта 2019

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

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 Memory Model :

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

Любое, я прав, или можно написать такой код безопасно?

1 Ответ

7 голосов
/ 09 марта 2019

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

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


Результаты программы не определены.У вас есть гонка данных на карте 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: Data Race Detector

...