Sobes.tech
Middle

What is a mutex, what types of mutexes exist, and how are they used?

sobes.tech AI

Answer from AI

Mutex (from the English MUTual EXclusion) is a synchronization primitive used to ensure exclusive access to a shared resource between competing goroutines. It guarantees that at any given moment, only one goroutine can own the mutex and, consequently, access the protected data.

In Go, the sync package provides two main types of mutexes:

  • sync.Mutex: A standard mutex. It provides Lock() and Unlock() methods. If the mutex is already locked, calling Lock() blocks the current goroutine until it is released. Unlock() releases the mutex.

    var mu sync.Mutex
    var counter int
    
    func increment() {
        mu.Lock() // Lock access to counter
        counter++
        mu.Unlock() // Unlock access
    }
    
  • sync.RWMutex: A read/write mutex. It allows multiple goroutines to read the protected resource simultaneously but only one to write. It provides Lock(), Unlock() (for writing), RLock(), and RUnlock() (for reading).

    var rwMu sync.RWMutex
    var data []int
    
    func readData() {
        rwMu.RLock() // Lock for reading
        // Read data
        fmt.Println(data)
        rwMu.RUnlock() // Unlock reading
    }
    
    func writeData(val int) {
        rwMu.Lock() // Lock for writing
        // Write to data
        data = append(data, val)
        rwMu.Unlock() // Unlock writing
    }
    

Using mutexes is necessary to prevent race conditions, where multiple goroutines attempt to modify the same data simultaneously, leading to unpredictable results.

Usage rules:

  1. Call Lock() before accessing the protected resource.
  2. Call Unlock() after finishing access. It is recommended to use defer mu.Unlock() to ensure unlocking even in case of panic.
  3. Do not attempt to lock a mutex that is already locked from the same goroutine (may lead to deadlock).
  4. Do not attempt to unlock an unlocked mutex.
  5. When using RWMutex, use RLock/RUnlock for read operations and Lock/Unlock for write operations.
  6. Mutexes should not be copied after first use. Pass them by reference or pointer.

Example of using defer:

func safeIncrement() {
    mu.Lock()
    defer mu.Unlock() // Ensures unlocking
    counter++
}