如果键总是唯一的,同时写入 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
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