💻 Tech
Yesterday, I wrote about mutex, and how it can help maintain concurrency safety in Go. Now, I will show you a simple demo:
// main.go
package main
import (
"fmt"
"sync"
)
func main() {
var wg sync.WaitGroup
wg.Add(10)
repo := &Repo{}
go func() {
for i := 1; i <= 10; i++ {
go func(i int) {
defer wg.Done()
repo.Add("id", fmt.Sprintf("%d", i), i)
fmt.Printf("%d - current repo value: %v\n", i, repo.values)
}(i)
}
}()
wg.Wait()
}
// repo.go
package main
import (
"fmt"
"sync"
"time"
)
type Repo struct {
values map[string]string
mutex sync.Mutex
}
func (r *Repo) Add(id string, val string, sleepSec int) {
defer r.mutex.Unlock()
r.mutex.Lock()
fmt.Printf("%d - waiting for %d seconds\n", sleepSec, 10-sleepSec)
time.Sleep(time.Duration(10-sleepSec) * time.Second)
if r.values == nil {
r.values = make(map[string]string)
}
r.values[id] = val
}
# Output:
➜ go run .
10 - waiting for 0 seconds
5 - waiting for 5 seconds
10 - current repo value: map[id:10]
5 - current repo value: map[id:5]
1 - waiting for 9 seconds
1 - current repo value: map[id:1]
2 - waiting for 8 seconds
2 - current repo value: map[id:2]
7 - waiting for 3 seconds
7 - current repo value: map[id:7]
6 - waiting for 4 seconds
6 - current repo value: map[id:6]
8 - waiting for 2 seconds
8 - current repo value: map[id:8]
9 - waiting for 1 seconds
9 - current repo value: map[id:9]
3 - waiting for 7 seconds
3 - current repo value: map[id:3]
4 - waiting for 6 seconds
4 - current repo value: map[id:4]
Explanation:
- The main function creates 10 goroutines with each goroutine calls the repo’s Add method
- The Add method locks the mutex and waits for 10-i seconds (i.e., if current iteration is 1, then it blocks the execution for 9 seconds)
- The sleep simulates a process that takes some time to complete but requires a lock on the resource
- In the output, each goroutine doesn’t complete until the lock is released