Skip to content

Latest commit

 

History

History

01_mutex

🔒 Mutex and RWMutex in Go

💡 What is a Mutex?

A mutex (short for mutual exclusion) is a synchronization primitive used to ensure that only one goroutine can access a critical section of code at a time. This prevents data races and ensures the integrity of shared resources.

📝 Types of Mutexes in Go

  1. sync.Mutex

    • This is the basic mutex type. It supports exclusive locking (i.e., only one goroutine can lock it at a time).
  2. sync.RWMutex (Read/Write Mutex)

    • This is a more advanced form of a mutex, which allows multiple readers but only one writer. It is useful when a resource is often read but rarely modified.

✅ sync.Mutex

1. Creating and Using a Mutex

A Mutex from the sync package is typically used to protect shared data by preventing multiple goroutines from accessing the same data at the same time.

import "sync"

var mu sync.Mutex

2. Locking and Unlocking a Mutex

You can lock and unlock a mutex with the Lock() and Unlock() methods.

mu.Lock()   // Acquire the lock
// Critical section
mu.Unlock() // Release the lock

3. Example with Mutex

package main

import (
	"fmt"
	"sync"
)

var mu sync.Mutex
var counter int

func increment() {
	mu.Lock()   // Acquire the lock
	counter++   // Increment the counter
	mu.Unlock() // Release the lock
}

func main() {
	var wg sync.WaitGroup

	for i := 0; i < 10; i++ {
		wg.Add(1)
		go func() {
			defer wg.Done()
			increment()
		}()
	}

	wg.Wait()
	fmt.Println("Final counter:", counter) // Output: Final counter: 10
}

🔑 Key Concepts with Mutex

  • Always unlock a locked mutex: If you forget to unlock a mutex, it will block other goroutines indefinitely, causing a deadlock.
  • Defer Unlocking: You can use defer to ensure the mutex is unlocked even if an error occurs in the critical section.

✅ sync.RWMutex (Read/Write Mutex)

sync.RWMutex is a more specialized form of a mutex, which allows multiple goroutines to acquire the lock for reading concurrently, but only one goroutine can acquire the lock for writing. This is useful when you have more read operations than write operations, and you want to maximize concurrency.

1. Creating and Using a RWMutex

import "sync"

var rwMu sync.RWMutex

2. Locking and Unlocking the RWMutex

  • RLock(): Acquires the lock for reading (shared access).
  • Lock(): Acquires the lock for writing (exclusive access).
  • Unlock(): Releases the lock.
rwMu.RLock()   // Acquire a read lock (shared lock)
rwMu.RUnlock() // Release the read lock

rwMu.Lock()    // Acquire a write lock (exclusive lock)
rwMu.Unlock()  // Release the write lock

3. Example with RWMutex

package main

import (
	"fmt"
	"sync"
)

var rwMu sync.RWMutex
var counter int

// Read function
func readCounter() int {
	rwMu.RLock()   // Acquire a read lock
	defer rwMu.RUnlock() // Ensure the lock is released
	return counter
}

// Write function
func incrementCounter() {
	rwMu.Lock()   // Acquire a write lock
	defer rwMu.Unlock() // Ensure the lock is released
	counter++
}

func main() {
	var wg sync.WaitGroup

	// Start 5 goroutines to read the counter
	for i := 0; i < 5; i++ {
		wg.Add(1)
		go func() {
			defer wg.Done()
			fmt.Println("Counter value:", readCounter())
		}()
	}

	// Start 1 goroutine to increment the counter
	wg.Add(1)
	go func() {
		defer wg.Done()
		incrementCounter()
		fmt.Println("Counter incremented.")
	}()

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

4. When to Use RWMutex?

  • Read-Heavy Workloads: Use RWMutex when you have many goroutines that need to read data but only occasionally need to write to it. This allows you to maximize concurrency for read operations.
  • Write-Heavy Workloads: If the resource is frequently modified, a sync.Mutex might be a better choice, as RWMutex introduces overhead for managing read and write locks.

🔑 Key Concepts with RWMutex

  • Read Lock (RLock): Multiple goroutines can hold the read lock simultaneously, but no goroutine can write when a read lock is held.
  • Write Lock (Lock): Only one goroutine can hold the write lock at a time, and it prevents any other goroutine from reading or writing.

📚 Best Practices

  • Use sync.Mutex when you have exclusive access to shared resources. It’s simpler and works well for small code sections with fewer goroutines.
  • Use sync.RWMutex when you have frequent read operations and occasional writes. It can improve performance by allowing multiple readers to access shared data concurrently while ensuring exclusive access for writers.
  • Avoid Deadlocks: Always be cautious when locking multiple mutexes. Lock them in the same order to avoid circular waiting, which could lead to a deadlock.