如果键总是唯一的,同时写入 Golang 映射是否安全?

Is it safe to write to a Golang map concurrently if the keys are always unique?

package main

import (
    "sync"
    "fmt"
)

var m = make(map[string]string)

var seen = make(map[string]bool)

func main() {
    wg := new(sync.WaitGroup)
    wg.Add(1)
    ch := make(chan string)
    go deduplicate(ch, wg)

    toAdd := []string{"foo", "bar", "baz", "foo"}
    for _, s := range toAdd {
        ch <- s
    }
    wg.Wait()
    fmt.Println(m)
}

func deduplicate(ch chan string, wg *sync.WaitGroup) {
    for s := range ch {
        if seen[s] {
            wg.Done()
            continue
        }
        seen[s] = true
        go write(s)
    }
}

func write(s string) {
    m[s] = "written"   
}

上面的代码可以安全使用吗?请注意,多个字符串将同时写入映射 m,但它们肯定是唯一值,因此不会多次写入任何字符串。

不,那不安全。键是否唯一并不重要。您需要避免并发写入或写入与读取并发。并发读取可以。

您可以使用竞争检测器来查找 运行 go run -race myprog.go 这样的问题。参见 https://golang.org/doc/articles/race_detector.html

不安全。 Go 映射是自组织数据结构。 运行 竞争检测器。

$ go run -race racer.go
==================
WARNING: DATA RACE
Write at 0x00c42007c150 by goroutine 8:
  runtime.mapassign_faststr()
      /home/peter/go/src/runtime/hashmap_fast.go:598 +0x0
  main.write()
      /home/peter/gopath/src/racer.go:38 +0x6e

Previous write at 0x00c42007c150 by goroutine 7:
  runtime.mapassign_faststr()
      /home/peter/go/src/runtime/hashmap_fast.go:598 +0x0
  main.write()
      /home/peter/gopath/src/racer.go:38 +0x6e

Goroutine 8 (running) created at:
  main.deduplicate()
      /home/peter/gopath/src/racer.go:33 +0x190

Goroutine 7 (finished) created at:
  main.deduplicate()
      /home/peter/gopath/src/racer.go:33 +0x190
==================
==================
WARNING: DATA RACE
Write at 0x00c42007c150 by goroutine 9:
  runtime.mapassign_faststr()
      /home/peter/go/src/runtime/hashmap_fast.go:598 +0x0
  main.write()
      /home/peter/gopath/src/racer.go:38 +0x6e

Previous write at 0x00c42007c150 by goroutine 7:
  runtime.mapassign_faststr()
      /home/peter/go/src/runtime/hashmap_fast.go:598 +0x0
  main.write()
      /home/peter/gopath/src/racer.go:38 +0x6e

Goroutine 9 (running) created at:
  main.deduplicate()
      /home/peter/gopath/src/racer.go:33 +0x190

Goroutine 7 (finished) created at:
  main.deduplicate()
      /home/peter/gopath/src/racer.go:33 +0x190
==================
==================
WARNING: DATA RACE
Read at 0x00c42007c150 by main goroutine:
  reflect.maplen()
      /home/peter/go/src/runtime/hashmap.go:1255 +0x0
  reflect.Value.MapKeys()
      /home/peter/go/src/reflect/value.go:1090 +0x421
  fmt.(*pp).printValue()
      /home/peter/go/src/fmt/print.go:739 +0x17fc
  fmt.(*pp).printArg()
      /home/peter/go/src/fmt/print.go:682 +0x19e
  fmt.(*pp).doPrintln()
      /home/peter/go/src/fmt/print.go:1136 +0x6e
  fmt.Fprintln()
      /home/peter/go/src/fmt/print.go:247 +0x65
  fmt.Println()
      /home/peter/go/src/fmt/print.go:257 +0x78
  main.main()
      /home/peter/gopath/src/racer.go:23 +0x1c3

Previous write at 0x00c42007c150 by goroutine 7:
  runtime.mapassign_faststr()
      /home/peter/go/src/runtime/hashmap_fast.go:598 +0x0
  main.write()
      /home/peter/gopath/src/racer.go:38 +0x6e

Goroutine 7 (finished) created at:
  main.deduplicate()
      /home/peter/gopath/src/racer.go:33 +0x190
==================
==================
WARNING: DATA RACE
Read at 0x00c42009a088 by main goroutine:
  reflect.typedmemmove()
      /home/peter/go/src/runtime/mbarrier.go:259 +0x0
  reflect.Value.MapIndex()
      /home/peter/go/src/reflect/value.go:1069 +0x232
  fmt.(*pp).printValue()
      /home/peter/go/src/fmt/print.go:750 +0x1867
  fmt.(*pp).printArg()
      /home/peter/go/src/fmt/print.go:682 +0x19e
  fmt.(*pp).doPrintln()
      /home/peter/go/src/fmt/print.go:1136 +0x6e
  fmt.Fprintln()
      /home/peter/go/src/fmt/print.go:247 +0x65
  fmt.Println()
      /home/peter/go/src/fmt/print.go:257 +0x78
  main.main()
      /home/peter/gopath/src/racer.go:23 +0x1c3

Previous write at 0x00c42009a088 by goroutine 7:
  main.write()
      /home/peter/gopath/src/racer.go:38 +0x84

Goroutine 7 (finished) created at:
  main.deduplicate()
      /home/peter/gopath/src/racer.go:33 +0x190
==================
map[foo:written bar:written baz:written]
Found 4 data race(s)
exit status 66