Skip to main content

Sync

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:

  1. Create two functions: write and read.
  2. Both functions should have two parameters - a map (map[string]int) and a mutex (sync.Mutex).
  3. Implement an infinite loop inside each function to simulate continuous operations.
  4. 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.
  5. 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.
  6. The main function should create a shared map and a mutex.
  7. Launch two goroutines: one for writing values and one for reading values.
  8. 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)
}