mdawar.dev

A blog about programming, Web development, Open Source, Linux and DevOps.

Go - Mutex

To avoid conflicts when multiple goroutines access the same variable, a sync.Mutex lock can be used to synchronize shared memory access.

go
// The zero value for a `Mutex` is an unlocked mutex.
// A `Mutex` must not be copied after first use.
var mu sync.Mutex

// Acquire a lock, called by the goroutine accessing a shared memory.
// If it is already locked, the call will block until the lock becomes available.
mu.Lock()

// The region between the `Lock` and `Unlock` is used to safely access shared memory.
// This is called a critical section.
i++ // Modify a variable safely without conflicts

// Release the lock, called by the goroutine when finished accessing shared memory.
// If the mutex is not locked, `Unlock` will cause a run-time error.
mu.Unlock()

func modify() {
  mu.Lock()
  // In a function, the `Unlock` call can be deferred.
  defer mu.Unlock()

  i++
}

Example:

go
package main

import (
	"fmt"
	"sync"
)

var count int     // Shared variable
var mu sync.Mutex // Mutex to guard the shared variable

func main() {
  var wg sync.WaitGroup

  // Create 1000 goroutines that each increment the count by 1.
  for i := 0; i < 1000; i++ {
    wg.Add(1)

    go func() {
      mu.Lock()         // Acquire the lock
      defer mu.Unlock() // Release the lock
      defer wg.Done()   // Signal that the goroutine is done

      count++ // Modify the shared variable
    }()
  }

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

  fmt.Println(count) // 1000
}