First of all, let’s understand what mutex is and why we use it. Mutex is a locking mechanism that protects shared data in a multi-threaded program where multiple threads are accessing the data concurrently. If we don’t use mutex then race conditions might happen in the program that will lead to inconsistent data throughout the program.
There are two types of Mutex in Golang –
- sync.Mutex
It protects the shared data both in reading & writing. This means if one thread is reading/writing another thread can’t read/write into the data. And if there is multiple thread reading then the read will happen one by one by each thread.
package main
import (
"fmt"
"sync"
"time"
)
type SyncData struct {
lock sync.Mutex
wg sync.WaitGroup
}
func main() {
// m := map[int]int{}
var sc SyncData
sc.wg.Add(7)
go readLoop(&sc)
go readLoop(&sc)
go readLoop(&sc)
go readLoop(&sc)
go writeLoop(&sc)
go writeLoop(&sc)
go writeLoop(&sc)
sc.wg.Wait()
}
func writeLoop(sc *SyncData) {
sc.lock.Lock()
time.Sleep(1 * time.Second)
fmt.Println("Write lock")
fmt.Println("Write unlock")
sc.lock.Unlock()
sc.wg.Done()
}
func readLoop(sc *SyncData) {
sc.lock.Lock()
time.Sleep(1 * time.Second)
fmt.Println("Read lock")
fmt.Println("Read unlock")
sc.lock.Unlock()
sc.wg.Done()
}
Here you can see the write will block both read/write and the read will block the write as well as read. [E.G – You can see the delay between the read print statements]
- Sync.RWMutex
Now we know if the data is the same and the system is read-heavy it is ok to allow multiple threads to read from the data as there won’t be any conflict. So we use RWMutex instead where the idea is any number of readers can acquire the read lock at the same time but only one writer will be able to acquire the write lock at a time.
package main
import (
"fmt"
"sync"
"time"
)
type SyncData struct {
lock sync.RWMutex
wg sync.WaitGroup
}
func main() {
// m := map[int]int{}
var sc SyncData
sc.wg.Add(7)
go readLoop(&sc)
go readLoop(&sc)
go readLoop(&sc)
go readLoop(&sc)
go writeLoop(&sc)
go writeLoop(&sc)
go writeLoop(&sc)
sc.wg.Wait()
}
func writeLoop(sc *SyncData) {
sc.lock.Lock()
time.Sleep(1 * time.Second)
fmt.Println("Write lock")
fmt.Println("Write unlock")
sc.lock.Unlock()
sc.wg.Done()
}
func readLoop(sc *SyncData) {
sc.lock.RLock()
time.Sleep(1 * time.Second)
fmt.Println("Read lock")
fmt.Println("Read unlock")
sc.lock.RUnlock()
sc.wg.Done()
}
Here you can see the write is blocking read & write but read is not blocking any read. Multiple threads is able to read at the same time. [E.G. – You can see the delay is write print statement but you won’t see any delay in read print statement]