A nice, go-idiomatic way to use a shared card

Let's say I had a program with parallel access to the map, for example:

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 because map entries are not atomic. So I could use read / write mutexes

 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 directly use mutexes, not channels.

What is a more ideological way to implement this? Or is it one of those cases when a mutex is all you need?

+7
concurrency go
source share
4 answers

I think this is very important depending on your expectations of performance and how this card will be used in the end.

When I studied the same question, I came across this very useful article which should answer your question .

My personal answer is that you should use the default channels unless you really find the need to use a mutex. The central point of the idiomatic Go is that you don’t need to use mutexes and worry about locking if you stick to the more functional features of high-level channels. Remember Go motto: "Exchange memory, exchanging messages, not exchanging data through exchange."

Another tidbit is a very detailed overview of the various methods for building a secure map for parallel use in the Mark Summerfield Go book .

To highlight Rob Pike slide , one of the creators of Go:

Concurrency Simplifies Synchronization

  • No explicit synchronization required
  • The program structure is implicitly synchronized

When you go along the path of using the primitive as a mutex, since your program is more complex, it is difficult, but very difficult. You have been warned.

Also cited from the Golang website itself :

Concurrent programming in many environments is complicated by the subtleties required to implement proper access to shared variables. Go encourages another approach in which common values ​​are passed around through channels and, in fact, are never actively shared by separate threads of execution. Only one gorutin has access to a value at any time. This approach can take too far. The number of references can be best done by placing the mutex around an integer variable, for example. But as a high-level approach, using channels to control access simplifies the recording of clear, correct programs.

+8
source share

I would say that mutexes are suitable for this application. Wrap them in type to change your mind later. Pay attention to the sync.RWMutex attachment, which makes the lock more accurate.

 type thing struct { sync.RWMutex values map[string]int } func newThing() *thing { return &thing{ values: make(map[string]int), } } func (t *thing) Get(key string) int { t.RLock() defer t.RUnlock() return t.values[key] } func (t *thing) Put(key string, value int) { t.Lock() defer t.Unlock() t.values[key] = value } func main() { t := newThing() t.Put("hello", 1) t.Put("sausage", 2) fmt.Println(t.Get("hello")) fmt.Println(t.Get("potato")) } 

Playground

+7
source share
  • You cannot use locks as such for message queues. What are the channels for?

  • You can simulate channel locks, but for them the wrong channels.

  • Use locks for simultaneous secure access to shared resources.

  • Use feeds for simultaneous message queuing.

Use RWMutex to protect card entries.

+3
source share

Here's an alternative channel approach using the channel as a mutual exclusion mechanism:

 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 mountains can take and return this shared resource. However, unlike the solution with RWMutex , several readers can block each other.

0
source share

All Articles