Challenge: Perform a Concurrent Read-Write Operation on a Map in Go
Problem statement
You are tasked with creating a solution that involves two functions – one for reading and one for writing values to a shared map. Your goal is to ensure thread safety and avoid race conditions by using the sync.Mutex
in Go.
Requirements:
- Create two functions:
write
andread
. - Both functions should have two parameters - a map (
map[string]int
) and a mutex (sync.Mutex
). - Implement an infinite loop inside each function to simulate continuous operations.
- In the
write
function, use the mutex to lock the shared map, write a specified value to a given key, unlock the mutex, and print the key-value pair. - In the
read
function, use the mutex to lock the shared map, read the value associated with a specified key, unlock the mutex, and print the key-value pair. - The
main
function should create a shared map and a mutex. - Launch two goroutines: one for writing values and one for reading values.
- Ensure that the main goroutine stays alive to allow continuous execution of the other goroutines for some time.
Solution
Let’s walk through the problem step by step and write the code that performs the concurrent read and write operations on the map using mutexes only.
Create two functions read
and write
, both of which take two parameters of type map
and sync.Mutex
. The methods will look like this:
The methods will look like:
func read(sharedMap map[int]int, mu *sync.Mutex)
func write(sharedMap map[int]int, mu *sync.Mutex)
Add an infinite loop to see the output. If you don’t want to write an infinite loop, take a for loop with some significant value (a few thousand will work).
func read(sharedMap map[int]int, mu *sync.Mutex) {
for {
}
}
Now add the mutex lock. The lock is mandatory so that only one goroutine can access the critical section at a time. The critical section in our code occurs when the value is read and updated.
Lock while reading the value as follows:
mu.Lock()
value := sharedMap[0]
mu.Unlock()
Lock while updating the value. This can be completed with either of the code blocks below:
mu.Lock()
sharedMap[0] = sharedMap[0] + 1
mu.Unlock()
Or
mu.Lock()
sharedMap[0]++
mu.Unlock()
Let’s look at the complete solution by putting everything together.
package main
import (
"fmt"
"sync"
"time"
)
func read(sharedMap map[int]int, mu *sync.Mutex) {
for {
mu.Lock()
val := sharedMap[0]
fmt.Println(val)
mu.Unlock()
}
}
func write(sharedMap map[int]int, mu *sync.Mutex) {
for {
mu.Lock()
sharedMap[0] = sharedMap[0] + 1
mu.Unlock()
}
}
func main() {
var mu sync.Mutex
sharedMap := make(map[int]int)
sharedMap[0] = 0
go read(sharedMap, &mu)
go write(sharedMap, &mu)
time.Sleep(100 * time.Millisecond)
}