DevPrep
  • Interview Prep
  • Projects
  • Resources
  • Pricing
  • About Us
Submit Question
DevPrep
  • Pricing
  • About Us
Submit Question

Practice

  • JavaScript
  • DSA
  • Machine Coding
  • System Design

Resources

  • Learning Tracks
  • Articles
  • Roadmaps
  • Compare Concepts
  • Glossary
  • Developer Tools
  • All Questions

Company

  • About
  • Pricing

Legal

  • Privacy Policy
  • Terms of Service
DevPrep

© 2026 DevPrep. All rights reserved.

← Back to Questions
MediumTheory

Sync Package: Mutex, RWMutex, Once, Cond, and Atomics

18 views

Problem Statement

Master all synchronization primitives in Go's sync package for building thread-safe applications.

sync.Mutex vs sync.RWMutex

// Mutex: exclusive access
type SafeCounter struct {
    mu    sync.Mutex
    count int
}

func (c *SafeCounter) Inc() {
    c.mu.Lock()
    defer c.mu.Unlock()
    c.count++
}

// RWMutex: multiple readers OR one writer
type Cache struct {
    mu   sync.RWMutex
    data map[string]string
}

func (c *Cache) Get(key string) string {
    c.mu.RLock()
    defer c.mu.RUnlock()
    return c.data[key]
}

func (c *Cache) Set(key, value string) {
    c.mu.Lock()
    defer c.mu.Unlock()
    c.data[key] = value
}

sync.Once

var (
    instance *Database
    once     sync.Once
)

func GetDB() *Database {
    once.Do(func() {
        instance = &Database{}
        instance.Connect()
    })
    return instance
}

// Q: What if once.Do(f) panics?
// A: It counts as "done" - won't retry!

sync.WaitGroup

var wg sync.WaitGroup

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

wg.Wait() // Blocks until all Done() called

sync.Cond

// Broadcast to multiple waiting goroutines
type Barrier struct {
    cond  *sync.Cond
    ready bool
}

func NewBarrier() *Barrier {
    return &Barrier{
        cond: sync.NewCond(&sync.Mutex{}),
    }
}

func (b *Barrier) Wait() {
    b.cond.L.Lock()
    for !b.ready {
        b.cond.Wait() // Releases lock while waiting
    }
    b.cond.L.Unlock()
}

func (b *Barrier) Release() {
    b.cond.L.Lock()
    b.ready = true
    b.cond.Broadcast() // Wake all waiters
    b.cond.L.Unlock()
}

sync/atomic

import "sync/atomic"

var counter int64

// Atomic increment (faster than mutex for simple ops)
atomic.AddInt64(&counter, 1)

// Load/Store
val := atomic.LoadInt64(&counter)
atomic.StoreInt64(&counter, 100)

// Compare and Swap
swapped := atomic.CompareAndSwapInt64(&counter, 100, 200)

// atomic.Value for complex types
var config atomic.Value
config.Store(Config{Debug: true})
cfg := config.Load().(Config)

sync.Map

// Use when: write-once read-many, or disjoint key sets
var m sync.Map

m.Store("key", "value")
val, ok := m.Load("key")
m.Delete("key")

m.Range(func(key, value interface{}) bool {
    fmt.Println(key, value)
    return true // continue iteration
})

Key Interview Questions

  • Q: When sync.Map over regular map+mutex?
    A: Write-once read-many, or disjoint key sets per goroutine.
  • Q: Cost of atomic vs mutex?
    A: Atomics are hardware instructions (~5-10ns), mutex involves OS calls (~30-50ns).
  • Q: Is it safe to copy a sync.Mutex?
    A: NO! It copies lock state, causing deadlocks.

Sample Test Cases

Case 1
Input
var mu sync.Mutex; mu.Lock()
Expected Output
Acquires exclusive lock
Case 2
Input
sync.Once.Do(f)
Expected Output
f executes exactly once across goroutines

No solutions yet

Be the first to share a solution for this question.

Comments (0)

Sign in to leave a comment.

No comments yet. Be the first to comment.

Stats

Views
18
Likes
0
Solutions
0
Comments
0

Category

Backend Engineering

Languages

Go