Part 1 , Part 2 , Part 3 , Part 4 , Part 5

In Go, critical sections refer to parts of your code that manipulate a resource which must be accessed by only one goroutine at a time. By using a mutex, you can temporarily lock a critical section, perform the operation, and then unlock it, allowing other goroutines to access the resource. Attempting to lock a critical section in a goroutine will block execution until the lock can be acquired.

Playground: https://go.dev/play/p/OEd7jdOA_Mw

package main

import (
	"fmt"
	"sync"
)

func main() {
	// create a map to store the products of two numbers
	// with the numbers in string form as a key
	productMap := map[string]int{}

	// create a mutex to protect the map
	// since only one goroutine can access the map at a time
	mu := sync.Mutex{}
	wg := sync.WaitGroup{}
	// use many goroutines to compute the product of two numbers
	for i := 0; i < 10; i++ {
		for j := 0; j < 10; j++ {
			copyI, copyJ := i, j
			wg.Add(1)
			go func() {
				defer wg.Done()
				// compute the product
				key := fmt.Sprintf("%d,%d", copyI, copyJ)
				product := copyI * copyJ

				// this is a critical section
				// only one goroutine can access the map at a time
				// this is a common pattern to protect a shared resource
				// with a mutex
				mu.Lock()
				productMap[key] = product
				mu.Unlock()
			}()
		}
	}

	// wait for all the goroutines to finish
	wg.Wait()

	// print the products from the map
	for key, product := range productMap {
		fmt.Println(key, product)
	}
	fmt.Println("Done", len(productMap))
}