Say I had a program with concurrent access to a map, like this:
func getKey(r *http.Request) string { ... }
values := make(map[string]int)
http.HandleFunc("/get", func(w http.ResponseWriter, r *http.Request) {
key := getKey(r)
fmt.Fprint(w, values[key])
})
http.HandleFunc("/set", func(w http.ResponseWriter, r *http.Request) {
key := getKey(r)
values[key] = rand.Int()
})
This is bad since map writes are non-atomic. So I could use a read/write mutex
func getKey(r *http.Request) string { ... }
values := make(map[string]int)
var lock sync.RWMutex
http.HandleFunc("/get", func(w http.ResponseWriter, r *http.Request) {
key := getKey(r)
lock.RLock()
fmt.Fprint(w, values[key])
lock.RUnlock()
})
http.HandleFunc("/set", func(w http.ResponseWriter, r *http.Request) {
key := getKey(r)
lock.Lock()
values[key] = rand.Int()
lock.Unlock()
})
Which seems fine except for the fact that we're directly using mutexes and not channels.
What's a more go-idiomatic way of implementing this? Or is this one of those times where a mutex is really all you need?
It's important to note that maps in go are not safe for concurrent use. Maps are not safe for concurrent use: it's not defined what happens when you read and write to them simultaneously.
Maps are used to store data values in key:value pairs. Each element in a map is a key:value pair. A map is an unordered and changeable collection that does not allow duplicates. The length of a map is the number of its elements.
In Go language, a map is a powerful, ingenious, and versatile data structure. Golang Maps is a collection of unordered pairs of key-value. It is widely used because it provides fast lookups and values that can retrieve, update or delete with the help of keys. It is a reference to a hash table.
Go by Example: Maps Maps are Go's built-in associative data type (sometimes called hashes or dicts in other languages). To create an empty map, use the builtin make : make(map[key-type]val-type) . Set key/value pairs using typical name[key] = val syntax. Printing a map with e.g. fmt.
Here's an alternative channel-based approach, using the channel as a mechanism for mutual exclusion:
func getKey(r *http.Request) string { ... }
values_ch := make(chan map[string]int, 1)
values_ch <- make(map[string]int)
http.HandleFunc("/get", func(w http.ResponseWriter, r *http.Request) {
key := getKey(r)
values := <- values_ch
fmt.Fprint(w, values[key])
values_ch <- values
})
http.HandleFunc("/set", func(w http.ResponseWriter, r *http.Request) {
key := getKey(r)
values := <- values_ch
values[key] = rand.Int()
values_ch <- values
})
where we initially put the resource in a shared channel. Then the goroutines can borrow and return that shared resource. However, unlike the solution with RWMutex
, multiple readers can block each other.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With