Files
bootdotdev-fcc-learn-golang…/course/14-mutexes/exercises/4-mutex_review/readme.md
wagslane 9be3074de6 first
2023-05-01 15:25:27 -06:00

98 lines
2.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Mutex Review
The principle problem that mutexes help us avoid is the *concurrent read/write problem*. This problem arises when one thread is writing to a variable while another thread is reading from that same variable *at the same time*.
When this happens, a Go program will panic because the reader could be reading bad data while it's being mutated in place.
![mutex](https://i.imgur.com/NGBnMXe.png)
## Mutex example
```go
package main
import (
"fmt"
)
func main() {
m := map[int]int{}
go writeLoop(m)
go readLoop(m)
// stop program from exiting, must be killed
block := make(chan struct{})
<-block
}
func writeLoop(m map[int]int) {
for {
for i := 0; i < 100; i++ {
m[i] = i
}
}
}
func readLoop(m map[int]int) {
for {
for k, v := range m {
fmt.Println(k, "-", v)
}
}
}
```
The example above creates a map, then starts two goroutines which each have access to the map. One goroutine continuously mutates the values stored in the map, while the other prints the values it finds in the map.
If we run the program on a multi-core machine, we get the following output: `fatal error: concurrent map iteration and map write`
In Go, it isnt safe to read from and write to a map at the same time.
## Mutexes to the rescue
```go
package main
import (
"fmt"
"sync"
)
func main() {
m := map[int]int{}
mux := &sync.Mutex{}
go writeLoop(m, mux)
go readLoop(m, mux)
// stop program from exiting, must be killed
block := make(chan struct{})
<-block
}
func writeLoop(m map[int]int, mux *sync.Mutex) {
for {
for i := 0; i < 100; i++ {
mux.Lock()
m[i] = i
mux.Unlock()
}
}
}
func readLoop(m map[int]int, mux *sync.Mutex) {
for {
mux.Lock()
for k, v := range m {
fmt.Println(k, "-", v)
}
mux.Unlock()
}
}
```
In this example, we added a `sync.Mutex{}` and named it `mux`. In the write loop, the `Lock()` method is called before writing, and then the `Unlock()` is called when we're done. This Lock/Unlock sequence ensures that no other threads can `Lock()` the mutex while *we* have it locked any other threads attempting to `Lock()` will block and wait until we `Unlock()`.
In the reader, we `Lock()` before iterating over the map, and likewise `Unlock()` when we're done. Now the threads share the memory safely!