Sobes.tech
Middle+

What happens during concurrent writing to a map and how can this problem be solved?

sobes.tech AI

Answer from AI

In concurrent writing to a map without synchronization, a data race will occur. This can lead to unpredictable program behavior, including panics (race detection) or incorrect map state.

The problem is solved by using synchronization mechanisms:

  1. sync.Mutex or sync.RWMutex: Locking access to the entire map or dividing access into read/write.

    import "sync"
    
    type SafeMap struct {
        mu sync.Mutex // or sync.RWMutex
        m  map[string]int
    }
    
    func (s *SafeMap) Store(key string, value int) {
        s.mu.Lock()
        defer s.mu.Unlock()
        s.m[key] = value
    }
    
    func (s *SafeMap) Load(key string) (int, bool) {
        s.mu.Lock() // or s.mu.RLock() for RWMutex
        defer s.mu.Unlock() // or s.mu.RUnlock()
        val, ok := s.m[key]
        return val, ok
    }
    
  2. sync.Map: A specialized map type optimized for concurrent access, especially when keys are rarely updated but frequently read. It does not require explicit mutex usage.

    import "sync"
    
    var m sync.Map
    
    func Store(key string, value int) {
        m.Store(key, value)
    }
    
    func Load(key string) (int, bool) {
        val, ok := m.Load(key)
        if !ok {
            return 0, false // Key not found
        }
        // Type assertion needed as Load returns interface{}
        iVal, iOk := val.(int)
        return iVal, iOk
    }
    

Comparison of sync.Mutex / sync.RWMutex and sync.Map:

Feature sync.Mutex / sync.RWMutex sync.Map
Management Explicit (must lock) Implicit (built-in logic)
Performance May be lower with frequent locking of entire access Higher with many reads and few modifications
API Uses standard map with external mutex Specialized methods (Store, Load, LoadAndDelete, Range)
Return type of Load Map element type interface{} (requires casting)
Memory usage Standard map + mutex May use more memory due to internal structure
Element deletion delete(m, key) LoadAndDelete(key)

Choosing between sync.Mutex (sync.RWMutex) and sync.Map depends on the map usage pattern: if concurrent writes are frequent, a regular mutex may be simpler and clearer. If reads far outnumber writes, sync.RWMutex or sync.Map can offer performance benefits. sync.Map is often preferred when the map is used as a cache (many reads, few writes/deletes).