Russel
Russel

Reputation: 115

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"   
}

Is the code above safe to use? Note that multiple strings will be written to the map m concurrently, but they are sure to be unique values, so no string will be written more than once.

Upvotes: 10

Views: 5968

Answers (3)

Dashen
Dashen

Reputation: 17

The essence of this problem is that it may expand

Upvotes: -1

Andy Schweig
Andy Schweig

Reputation: 6749

No, that is not safe. It doesn't matter if the keys are unique. You need to avoid concurrent writes or writes concurrent with reads. Concurrent reads are OK.

You can use the race detector to find problems like this by running go run -race myprog.go. See https://golang.org/doc/articles/race_detector.html.

Upvotes: 6

peterSO
peterSO

Reputation: 166549

It is not safe. Go maps are are self-organizing data structures. Run the race detector.

$ 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

Upvotes: 3

Related Questions