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