This commit is contained in:
wagslane
2023-05-01 15:25:27 -06:00
parent f8912668b8
commit 9be3074de6
868 changed files with 58698 additions and 2 deletions

View File

@@ -0,0 +1,9 @@
{
"question": "Why would you use a mutex?",
"answers": [
"To safely access a data structure concurrently",
"To protect data from network access",
"To stop other packages from using my code",
"To encapsulate private data members of a struct"
]
}

View File

@@ -0,0 +1,97 @@
# 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!