Sobes.tech
Middle+

How to protect against errors when writing to a map concurrently?

sobes.tech AI

Answer from AI

To protect against errors during concurrent writes to a map in Golang, which are not thread-safe by default, the following mechanisms are used:

  • sync.Mutex: Lock the map before writing and unlock after.

    import "sync"
    
    type SafeMap struct {
        mu sync.Mutex
        data map[string]int
    }
    
    func NewSafeMap() *SafeMap {
        return &SafeMap{
            data: make(map[string]int),
        }
    }
    
    func (sm *SafeMap) Set(key string, value int) {
        sm.mu.Lock()
        defer sm.mu.Unlock()
        sm.data[key] = value
    }
    
    func (sm *SafeMap) Get(key string) (int, bool) {
        sm.mu.Lock()
        defer sm.mu.Unlock()
        val, ok := sm.data[key]
        return val, ok
    }
    
  • sync.RWMutex: Allows multiple goroutines to read from the map simultaneously but blocks writing during concurrent read or write.

    import "sync"
    
    type SafeMapRWMutex struct {
        mu sync.RWMutex
        data map[string]int
    }
    
    func NewSafeMapRWMutex() *SafeMapRWMutex {
        return &SafeMapRWMutex{
            data: make(map[string]int),
        }
    }
    
    func (sm *SafeMapRWMutex) Set(key string, value int) {
        sm.mu.Lock() // Write lock
        defer sm.mu.Unlock()
        sm.data[key] = value
    }
    
    func (sm *SafeMapRWMutex) Get(key string) (int, bool) {
        sm.mu.RLock() // Read lock
        defer sm.mu.RUnlock()
        val, ok := sm.data[key]
        return val, ok
    }
    
  • sync.Map: A specialized thread-safe type optimized for scenarios where the set of keys is relatively stable and writes are rare, or when multiple goroutines read and write for disjoint sets of keys.

    import "sync"
    
    var safeMap sync.Map // Declaration
    
    func UseSyncMap() {
        safeMap.Store("key1", 10) // Write
        if val, ok := safeMap.Load("key1"); ok { // Read
            // Use val
        }
        safeMap.Delete("key1") // Delete
    }
    

Comparison of approaches:

Mechanism Advantages Disadvantages Use case
sync.Mutex Simple to use Blocks all operations during write Simple scenarios with low concurrency or roughly equal read/write.
sync.RWMutex Allows parallel reading More complex than sync.Mutex. Write blocks reads and other writes. Scenarios with frequent reads and rare writes.
sync.Map Optimized for specific scenarios Has a limited API. May be slower than Mutex if access is fully random. Scenarios with relatively stable keys or disjoint key sets for different goroutines.

The choice of approach depends on the nature of concurrent access to the map. For general cases, sync.Mutex or sync.RWMutex are often sufficient. For specific scenarios, sync.Map can provide better performance.