Skip to Content
Go Realm v1 is released 🎉
Go RoutinesWaitGroups & Mutex (Synchronization) & Select

🧠 WaitGroups & Mutex (Synchronization)

đŸŽ¯ āϞāĻ•ā§āĻˇā§āϝ

āφāϜ āϤ⧁āĻŽāĻŋ āĻļāĻŋāĻ–āĻŦ⧇ āϕ⧀āĻ­āĻžāĻŦ⧇ āĻāĻ•āĻžāϧāĻŋāĻ• goroutine āĻāĻ•āϏāĻžāĻĨ⧇ āϚāϞāϞ⧇āĻ“ āĻĒā§āϰ⧋āĻ—ā§āϰāĻžāĻŽ safe āĻāĻŦāĻ‚ predictable āϰāĻžāĻ–āĻž āϝāĻžā§ŸāĨ¤ āĻŽā§‚āϞāϤ āφāĻŽāϰāĻž āĻļāĻŋāĻ–āĻŦā§‹:

  • WaitGroup
  • Mutex / RWMutex
  • Race Condition
  • Race Detector (go run -race)
  • Channels āĻŦāύāĻžāĻŽ Mutex āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āĻŦ⧇ āωāĻĒāϝ⧁āĻ•ā§āϤ

āϕ⧇āύ synchronization āĻĻāϰāĻ•āĻžāϰ?

Go-āϤ⧇ goroutine āϗ⧁āϞ⧋ āĻāĻ•āχ āϏāĻžāĻĨ⧇ āϚāϞāϤ⧇ āĻĒāĻžāϰ⧇āĨ¤

āĻ•āĻŋāĻ¨ā§āϤ⧁ āϝāĻĻāĻŋ āϤāĻžāϰāĻž shared data āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰ⧇, āϤāĻ–āύ āϭ⧁āϞ āĻŽāĻžāύ, āĻ…āϏāĻžāĻŽāĻžā§āϜāĻ¸ā§āϝāĻĒā§‚āĻ°ā§āĻŖ āĻ…āĻŦāĻ¸ā§āĻĨāĻž, āĻ…āĻĨāĻŦāĻž race condition āϘāϟāϤ⧇ āĻĒāĻžāϰ⧇āĨ¤

1) WaitGroup āϕ⧀?

sync.WaitGroup āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰāĻž āĻšā§Ÿ āĻāĻ•āĻžāϧāĻŋāĻ• goroutine āĻļ⧇āώ āĻšāĻ“ā§ŸāĻž āĻĒāĻ°ā§āϝāĻ¨ā§āϤ main function āϕ⧇ āĻ…āĻĒ⧇āĻ•ā§āώāĻž āĻ•āϰāĻžāύ⧋āϰ āϜāĻ¨ā§āϝāĨ¤

WaitGroup āĻĻāĻŋāϝāĻŧ⧇ āφāĻŽāϰāĻž āĻŦāϞāϤ⧇ āĻĒāĻžāϰāĻŋ:

“āĻāχ X āϏāĻ‚āĻ–ā§āϝāĻ• goroutine āĻļ⧇āώ āύāĻž āĻšāĻ“āϝāĻŧāĻž āĻĒāĻ°ā§āϝāĻ¨ā§āϤ main() āĻŦāĻ¨ā§āϧ āĻšāĻŦ⧇ āύāĻžāĨ¤â€

🔹 āĻŽā§‚āϞ āϤāĻŋāύāϟāĻŋ āĻŽā§‡āĻĨāĻĄ

āĻŽā§‡āĻĨāĻĄāĻ•āĻžāϜ
Add(n)n āϟāĻŋ goroutine āĻļ⧁āϰ⧁ āĻšāĻŦ⧇ āϤāĻž WaitGroup-āϕ⧇ āϜāĻžāύāĻžāύ⧋
Done()āĻāĻ•āϟāĻŋ goroutine āĻ•āĻžāϜ āĻļ⧇āώ āϤāĻž WaitGroup-āϕ⧇ āϜāĻžāύāĻžāύ⧋
Wait()āϏāĻŦ Done() āύāĻž āĻšāĻ“āϝāĻŧāĻž āĻĒāĻ°ā§āϝāĻ¨ā§āϤ main() āĻ…āĻĒ⧇āĻ•ā§āώāĻž āĻ•āϰ⧇

🔸 āωāĻĻāĻžāĻšāϰāĻŖ: ā§ĢāϟāĻŋ goroutine āϚāĻžāϞāĻŋāϝāĻŧ⧇ WaitGroup āĻĻāĻŋāϝāĻŧ⧇ āĻ…āĻĒ⧇āĻ•ā§āώāĻž

package main import ( "fmt" "sync" ) func main() { var wg sync.WaitGroup wg.Add(5) // ā§ĢāϟāĻž goroutine āĻšāĻŦ⧇ for i := 1; i <= 5; i++ { go func(id int) { defer wg.Done() fmt.Println("Goroutine āϚāϞāϛ⧇:", id) }(i) } wg.Wait() // āϏāĻŦ goroutine āĻļ⧇āώ āύāĻž āĻšāĻ“āϝāĻŧāĻž āĻĒāĻ°ā§āϝāĻ¨ā§āϤ āĻ…āĻĒ⧇āĻ•ā§āώāĻž fmt.Println("āϏāĻŦ goroutine āĻļ⧇āώ āĻšāϝāĻŧ⧇āϛ⧇ ✅") }

🧩 āϕ⧀ āĻšāϞ⧋ āĻāĻ–āĻžāύ⧇: WaitGroup main function āϕ⧇ āĻŦā§āϞāĻ• āĻ•āϰ⧇ āϰāĻžāĻ–āϛ⧇ āϝāϤāĻ•ā§āώāĻŖ āύāĻž āϏāĻŦ goroutine āĻļ⧇āώ āĻšā§ŸāĨ¤


2) Shared Data + Mutex

Mutex-āĻāϰ āĻĒā§‚āĻ°ā§āĻŖāϰ⧂āĻĒ āĻšāϞ⧋ “Mutual Exclusion” (āĻĒāĻžāϰāĻ¸ā§āĻĒāϰāĻŋāĻ• āĻŦāĻ°ā§āϜāύ)āĨ¤

āĻāϰ āĻŽā§‚āϞ āϧāĻžāϰāĻŖāĻž āĻšāϞ⧋: āĻāĻ•āχ āϏāĻŽāϝāĻŧ⧇ āĻļ⧁āϧ⧁āĻŽāĻžāĻ¤ā§āϰ āĻāĻ•āϟāĻŋ goroutine-āϕ⧇ āĻāĻ•āϟāĻŋ āύāĻŋāĻ°ā§āĻĻāĻŋāĻˇā§āϟ āĻĄā§‡āϟāĻž (shared data) āĻ…ā§āϝāĻžāĻ•ā§āϏ⧇āϏ āĻŦāĻž āĻĒāϰāĻŋāĻŦāĻ°ā§āϤāύ āĻ•āϰāĻžāϰ āĻ…āύ⧁āĻŽāϤāĻŋ āĻĻ⧇āĻ“āϝāĻŧāĻžāĨ¤


âš ī¸ āϏāĻŽāĻ¸ā§āϝāĻž: Data Race

āϝāĻ–āύ āĻāĻ•āĻžāϧāĻŋāĻ• goroutine đŸ”ĨāĻāĻ•āχ datađŸ”Ĩ āĻāĻ•āϝ⧋āϗ⧇ āĻĒāϰāĻŋāĻŦāĻ°ā§āϤāύ āĻ•āϰāĻžāϰ āĻšā§‡āĻˇā§āϟāĻž āĻ•āϰ⧇, āϤāĻ–āύ data race āύāĻžāĻŽāĻ• āĻāĻ•āϟāĻŋ āĻŽāĻžāϰāĻžāĻ¤ā§āĻŽāĻ• āĻŦāĻžāĻ— āϤ⧈āϰāĻŋ āĻšā§ŸāĨ¤ āĻāϰ āĻĢāϞ⧇ āĻĄā§‡āϟāĻž āύāĻˇā§āϟ (corrupt) āĻšā§Ÿā§‡ āϝ⧇āϤ⧇ āĻĒāĻžāϰ⧇ āĻŦāĻž āĻ…ā§āϝāĻžāĻĒā§āϞāĻŋāϕ⧇āĻļāύāϟāĻŋ āĻ•ā§āĻ°ā§āϝāĻžāĻļ āĻ•āϰāϤ⧇ āĻĒāĻžāϰ⧇āĨ¤

🔒 āϏāĻŽāĻžāϧāĻžāύ: sync.Mutex

Go-āϤ⧇ āĻāχ data race āϠ⧇āĻ•āĻžāύ⧋āϰ āϜāĻ¨ā§āϝ sync āĻĒā§āϝāĻžāϕ⧇āĻœā§‡āϰ Mutex āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰāĻž āĻšāϝāĻŧāĨ¤ āĻāϟāĻŋ āĻāĻ•āϟāĻŋ āϤāĻžāϞāĻžāϰ (lock) āĻŽāϤ⧋ āĻ•āĻžāϜ āĻ•āϰ⧇āĨ¤

āĻāϰ āĻĻ⧁āϟāĻŋ āĻĒā§āϰāϧāĻžāύ āĻŽā§‡āĻĨāĻĄ āϰāϝāĻŧ⧇āϛ⧇:

  • Lock(): āĻĄā§‡āϟāĻž āĻ…ā§āϝāĻžāĻ•ā§āϏ⧇āϏ āĻ•āϰāĻžāϰ āφāϗ⧇ āĻāχ āĻŽā§‡āĻĨāĻĄ āĻ•āϞ āĻ•āϰāĻž āĻšā§ŸāĨ¤

    • āϝāĻĻāĻŋ āϤāĻžāϞāĻžāϟāĻŋ āĻ–ā§‹āϞāĻž āĻĨāĻžāϕ⧇, āϤāĻŦ⧇ goroutine-āϟāĻŋ āĻāϟāĻŋ āϞāĻ• āĻ•āϰ⧇ āĻāĻŦāĻ‚ āĻ•āĻžāϜ āĻļ⧁āϰ⧁ āĻ•āϰ⧇āĨ¤
    • āϝāĻĻāĻŋ āĻ…āĻ¨ā§āϝ goroutine āφāϗ⧇āχ āϞāĻ• āĻ•āϰ⧇ āϰāĻžāϖ⧇, āϤāĻŦ⧇ āĻāϟāĻŋ āϤāĻžāϞāĻž āĻ–ā§‹āϞāĻžāϰ āϜāĻ¨ā§āϝ āĻ…āĻĒ⧇āĻ•ā§āώāĻž (block) āĻ•āϰ⧇āĨ¤
  • Unlock(): āĻĄā§‡āϟāĻž āĻĒāϰāĻŋāĻŦāĻ°ā§āϤāύ āĻ•āϰāĻž āĻļ⧇āώ āĻšāϞ⧇ āĻāχ āĻŽā§‡āĻĨāĻĄ āĻ•āϞ āĻ•āϰāĻž āĻšā§ŸāĨ¤

    • āĻāϟāĻŋ āϤāĻžāϞāĻžāϟāĻŋ āϖ⧁āϞ⧇ āĻĻ⧇āϝāĻŧ, āϝāĻžāϤ⧇ āĻ…āĻĒ⧇āĻ•ā§āώāĻžāϰāϤ āĻ…āĻ¨ā§āϝ goroutine-āϗ⧁āϞāĻŋ āĻĄā§‡āϟāĻž āĻ…ā§āϝāĻžāĻ•ā§āϏ⧇āϏ āĻ•āϰāϤ⧇ āĻĒāĻžāϰ⧇āĨ¤

🔸 āωāĻĻāĻžāĻšāϰāĻŖ: 1000 āĻŦāĻžāϰ Counter āĻŦ⧃āĻĻā§āϧāĻŋ (Safe)

package main import ( "fmt" "sync" ) func main() { var counter = 0 var mu sync.Mutex var wg sync.WaitGroup wg.Add(1000) for i := 0; i < 1000; i++ { go func() { defer wg.Done() mu.Lock() counter++ // āĻāĻ–āύ āύāĻŋāϰāĻžāĻĒāĻĻ mu.Unlock() }() } wg.Wait() fmt.Println("Final Counter:", counter) }

🔒 āĻāĻ–āĻžāύ⧇ Mutex counter-āϕ⧇ āĻāĻ•āϏāĻžāĻĨ⧇ āĻāĻ• goroutine āĻĻā§āĻŦāĻžāϰāĻž āĻĒāϰāĻŋāĻŦāĻ°ā§āϤāύ āύāĻŋāĻļā§āϚāĻŋāϤ āĻ•āϰāϛ⧇āĨ¤


3) Mutex āĻŦāĻžāĻĻ āĻĻāĻŋāϞ⧇ āϕ⧀ āĻšāϝāĻŧ? (Race Condition!)

package main import ( "fmt" "sync" ) func main() { var counter = 0 var wg sync.WaitGroup wg.Add(1000) for i := 0; i < 1000; i++ { go func() { defer wg.Done() counter++ // ❌ āĻŦāĻŋāĻĒāĻĻ! āĻāĻ•āĻžāϧāĻŋāĻ• goroutine āĻāĻ•āϏāĻžāĻĨ⧇ āϞāĻŋāĻ–āϛ⧇ }() } wg.Wait() fmt.Println("Final Counter:", counter) }

đŸ’Ĩ Output āĻĒāϰāĻŋāĻŦāĻ°ā§āϤāύāĻļā§€āϞ āĻšāĻŦ⧇ — āĻ•āĻ–āύāĻ“ 800, āĻ•āĻ–āύāĻ“ 950, āĻ•āĻ–āύāĻ“ 1000 āύāĻžāĨ¤ āĻāϟāĻžāχ Race ConditionāĨ¤


4) Race Detector āĻŦā§āϝāĻŦāĻšāĻžāϰ

Go-āϤ⧇ āĻŦāĻŋāĻ˛ā§āϟ-āχāύ race detector āφāϛ⧇āĨ¤

go run -race main.go

āĻāϟāĻŋ āĻŦāϞāĻŦ⧇:

WARNING: DATA RACE Read at ... Previous write at ...

āĻāϰ āĻŽāĻžāύ⧇ āϤ⧋āĻŽāĻžāϰ āϕ⧋āĻĄā§‡ āĻāĻ•āĻžāϧāĻŋāĻ• goroutine āĻāĻ•āϏāĻžāĻĨ⧇ data āĻ…ā§āϝāĻžāĻ•ā§āϏ⧇āϏ āĻ•āϰāϛ⧇āĨ¤

🚨 Deadlock Prevention (āĻ…āϤāĻŋ āϗ⧁āϰ⧁āĻ¤ā§āĻŦāĻĒā§‚āĻ°ā§āĻŖ)

Deadlock āĻšāϝāĻŧ āϝāĻ–āύ āĻĻ⧁āϟāĻŋ goroutine āĻāϕ⧇ āĻ…āĻĒāϰ⧇āϰ lock-āĻāϰ āϜāĻ¨ā§āϝ āĻ…āĻĒ⧇āĻ•ā§āώāĻž āĻ•āϰ⧇āĨ¤

// ❌ DEADLOCK RISK: āĻ­āĻŋāĻ¨ā§āύ order-āĻ lock āύ⧇āĻ“āϝāĻŧāĻž func transfer1(mu1, mu2 *sync.Mutex) { mu1.Lock() mu2.Lock() // āĻāχ order // ... transfer logic mu2.Unlock() mu1.Unlock() } func transfer2(mu1, mu2 *sync.Mutex) { mu2.Lock() mu1.Lock() // āωāĻ˛ā§āĻŸā§‹ order = Deadlock! // ... transfer logic mu1.Unlock() mu2.Unlock() } // ✅ SOLUTION: āϏāĻŦāϏāĻŽāϝāĻŧ āĻāĻ•āχ order-āĻ lock āύāĻŋāύ func transfer(from, to *Account) { // Consistent ordering by memory address first, second := from, to if uintptr(unsafe.Pointer(from)) > uintptr(unsafe.Pointer(to)) { first, second = to, from } first.mu.Lock() defer first.mu.Unlock() second.mu.Lock() defer second.mu.Unlock() // Safe transfer from.balance -= amount to.balance += amount }

đŸ›Ąī¸ Deadlock Prevention Strategies (āĻĒā§āϰ⧋āĻĄāĻžāĻ•āĻļāύ⧇ āĻ…āĻŦāĻļā§āϝāχ āĻŽāĻžāύ⧁āύ)

StrategyāĻŦāĻ°ā§āĻŖāύāĻžāωāĻĻāĻžāĻšāϰāĻŖ
Lock OrderingāϏāĻŦāϏāĻŽāϝāĻŧ āĻāĻ•āχ sequence-āĻ lock āύāĻŋāύA→B→C (āĻ•āĻ–āύ⧋ C→A→B āύāϝāĻŧ)
TimeoutāύāĻŋāĻ°ā§āĻĻāĻŋāĻˇā§āϟ āϏāĻŽāϝāĻŧ⧇āϰ āĻŦ⧇āĻļāĻŋ wait āĻ•āϰāĻŦ⧇āύ āύāĻžTryLock() with timeout
Avoid Nested LocksāĻāĻ•āϏāĻžāĻĨ⧇ āĻāĻ•āĻžāϧāĻŋāĻ• lock āĻāĻĄāĻŧāĻŋāϝāĻŧ⧇ āϚāϞ⧁āύāϝāĻĻāĻŋ āϏāĻŽā§āĻ­āĻŦ āĻšāϝāĻŧ channel āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰ⧁āύ
Use deferāϏāĻŦāϏāĻŽāϝāĻŧ unlock āύāĻŋāĻļā§āϚāĻŋāϤ āĻ•āϰ⧁āύdefer mu.Unlock()

đŸ’ŧ Production Pattern: Thread-Safe Data Structure

āĻĒā§āϰ⧋āĻĄāĻžāĻ•āĻļāύ⧇ āφāĻŽāϰāĻž Mutex wrap āĻ•āϰ⧇ thread-safe struct āϤ⧈āϰāĻŋ āĻ•āϰāĻŋāĨ¤

package main import ( "fmt" "sync" ) // SafeCounter is a thread-safe counter type SafeCounter struct { mu sync.Mutex value int } // Increment safely increments the counter func (c *SafeCounter) Increment() { c.mu.Lock() defer c.mu.Unlock() c.value++ } // Value safely returns current value func (c *SafeCounter) Value() int { c.mu.Lock() defer c.mu.Unlock() return c.value } // Add safely adds n to counter func (c *SafeCounter) Add(n int) { c.mu.Lock() defer c.mu.Unlock() c.value += n } func main() { counter := &SafeCounter{} var wg sync.WaitGroup // 1000 goroutines safely increment for i := 0; i < 1000; i++ { wg.Add(1) go func() { defer wg.Done() counter.Increment() }() } wg.Wait() fmt.Println("Safe Counter:", counter.Value()) }

✅ Benefits:

  • Encapsulation: Mutex āĻŦāĻžāχāϰ⧇ āĻĨ⧇āϕ⧇ access āĻ•āϰāĻž āϝāĻžāϝāĻŧ āύāĻž
  • API clear: user āĻĻ⧇āĻ–āϤ⧇ āĻĒāĻžāϝāĻŧ āύāĻž internal locking
  • Reusable: āϝ⧇āϕ⧋āύ⧋ āĻĒā§āϰāĻœā§‡āĻ•ā§āĻŸā§‡ āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰāĻž āϝāĻžāϝāĻŧ

📊 Production Pattern: sync.Map (Built-in Thread-Safe Map)

āϝāĻĻāĻŋ map concurrent access āĻ•āϰāϤ⧇ āĻšāϝāĻŧ, sync.Map āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰ⧁āύ (Mutex āĻĻāĻŋāϝāĻŧ⧇ wrap āĻ•āϰāĻžāϰ āĻšā§‡āϝāĻŧ⧇ āĻ­āĻžāϞ⧋)āĨ¤

package main import ( "fmt" "sync" ) func main() { var m sync.Map var wg sync.WaitGroup // Write from multiple goroutines for i := 0; i < 100; i++ { wg.Add(1) go func(key int) { defer wg.Done() m.Store(key, key*2) }(i) } wg.Wait() // Read safely m.Range(func(key, value interface{}) bool { fmt.Printf("Key: %v, Value: %v\n", key, value) return true // continue iteration }) }

🆚 sync.Map āĻŦāύāĻžāĻŽ map + Mutex

ScenarioUseāĻ•āĻžāϰāĻŖ
Read-heavy (90%+)sync.MapBetter performance
Write-heavymap + MutexSimpler, better for writes
Key stable (rarely change)sync.MapOptimized for stable keys
Known keys at startmap + MutexSimpler initialization

📌 5) RWMutex (Reader-Writer Mutex)

## 🔐 RWMutex (Reader–Writer Mutex)

sync.RWMutex āĻšāϞ⧋ āĻāĻŽāύ āĻāĻ•āϟāĻŋ āϞāĻ• āϝāĻž readâŦ†ī¸ āĻŦ⧇āĻļāĻŋ āĻ“ writeâŦ‡ī¸ āĻ•āĻŽ āĻ•āĻžāĻœā§‡āϰ āĻ•ā§āώ⧇āĻ¤ā§āϰ⧇ āĻĒāĻžāϰāĻĢāϰāĻŽā§āϝāĻžāĻ¨ā§āϏ āĻŦāĻžā§œāĻžā§ŸāĨ¤


âš™ī¸ āĻ•āĻžāĻœā§‡āϰ āύāĻŋ⧟āĻŽ

RWMutex āĻĻ⧁āϟāĻŋ āφāϞāĻžāĻĻāĻž āϞāĻ• āĻĻā§‡ā§Ÿ:

  • RLock() → āĻāĻ•āĻžāϧāĻŋāĻ• goroutine āĻāĻ•āϏāĻžāĻĨ⧇ data read āĻ•āϰāϤ⧇ āĻĒāĻžāϰ⧇
  • Lock() → āĻļ⧁āϧ⧁āĻŽāĻžāĻ¤ā§āϰ āĻāĻ•āϟāĻŋ goroutine data write āĻ•āϰāϤ⧇ āĻĒāĻžāϰ⧇ (āϏāĻŦ readers āĻ“ writers āĻŦā§āϞāĻ• āĻšā§Ÿ)

📖 āωāĻĻāĻžāĻšāϰāĻŖ

var ( value int mu sync.RWMutex ) func reader(id int, wg *sync.WaitGroup) { defer wg.Done() mu.RLock() fmt.Println("Reader", id, "read:", value) mu.RUnlock() } func writer(wg *sync.WaitGroup) { defer wg.Done() mu.Lock() value++ fmt.Println("Writer updated value:", value) mu.Unlock() }

🔹 āĻāĻ–āĻžāύ⧇:

  • āĻāĻ•āĻžāϧāĻŋāĻ• reader() āĻāĻ•āχ āϏāĻŽā§Ÿā§‡ āϚāĻžāϞāϤ⧇ āĻĒāĻžāϰ⧇
  • āĻ•āĻŋāĻ¨ā§āϤ⧁ writer() āφāϏāϞ⧇, āĻ…āĻ¨ā§āϝ āϏāĻŦāĻžāχ đŸ”Ĩ āĻĨ⧇āĻŽā§‡ āϝāĻžā§Ÿ āϝāϤāĻ•ā§āώāĻŖ āύāĻž writer āĻ•āĻžāϜ āĻļ⧇āώ āĻ•āϰ⧇

⚡ āϕ⧇āύ RWMutex āĻĻāϰāĻ•āĻžāϰ?

āϧāϰ⧋ āϤ⧋āĻŽāĻžāϰ āĻĒā§āϰ⧋āĻ—ā§āϰāĻžāĻŽ ⧝ā§Ļ% āϏāĻŽā§Ÿ āĻļ⧁āϧ⧁ read āĻ•āϰ⧇, āφāϰ ā§§ā§Ļ% āϏāĻŽā§Ÿ write āĻ•āϰ⧇āĨ¤ āϝāĻĻāĻŋ āϏāĻžāϧāĻžāϰāĻŖ Mutex āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰ⧋, āϤāĻžāĻšāϞ⧇ āϏāĻŦ reader āĻāϕ⧇ āĻāϕ⧇ āϚāϞāĻŦ⧇ — āĻĒāĻžāϰāĻĢāϰāĻŽā§āϝāĻžāĻ¨ā§āϏ āĻ•āĻŽā§‡ āϝāĻžāĻŦ⧇āĨ¤

RWMutex āĻāχ āϏāĻŽāĻ¸ā§āϝāĻž āϏāĻŽāĻžāϧāĻžāύ āĻ•āϰ⧇:

  • āĻāĻ•āĻžāϧāĻŋāĻ• reader āĻāĻ•āϏāĻžāĻĨ⧇ read āĻ•āϰāϤ⧇ āĻĒāĻžāϰ⧇ (concurrent read)
  • āĻ•āĻŋāĻ¨ā§āϤ⧁ writer āφāϏāϞ⧇ āϏāĻŦāĻžāχ āĻŦā§āϞāĻ• āĻšāĻŦ⧇ āϝāϤāĻ•ā§āώāĻŖ āύāĻž writer āĻ•āĻžāϜ āĻļ⧇āώ āĻ•āϰ⧇

âš–ī¸ Mutex āĻŦāύāĻžāĻŽ RWMutex

āĻĻāĻŋāĻ•MutexRWMutex
āĻāĻ•āĻžāϧāĻŋāĻ• reader āĻāĻ•āϏāĻžāĻĨā§‡âŒ āύāĻžâœ… āĻšā§āϝāĻžāρ
āĻāĻ•āĻžāϧāĻŋāĻ• writer āĻāĻ•āϏāĻžāĻĨā§‡âŒ āύāĻžâŒ āύāĻž
Read-heavy workload āĻ performanceāϧ⧀āϰāĻĻā§āϰ⧁āϤ
āĻŦā§āϝāĻŦāĻšāĻžāϰShared data āĻ•āĻŽ āĻĒ⧜āĻž āĻšā§ŸShared data āĻŦ⧇āĻļāĻŋ āĻĒ⧜āĻž āĻšā§Ÿ

✅ āĻŽāύ⧇ āϰāĻžāĻ–ā§‹

RWMutex āϤāĻ–āύ āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰ⧋, āϝāĻ–āύ read āĻ…āύ⧇āĻ• āĻŦ⧇āĻļāĻŋ āφāϰ write āĻ•āĻŽāĨ¤ Write āĻāϞ⧇ āϏāĻŦāĻžāχ āĻ…āĻĒ⧇āĻ•ā§āώāĻž āĻ•āϰāĻŦ⧇; Read āĻāϞ⧇ āϏāĻŦāĻžāχ āĻāĻ•āϏāĻžāĻĨ⧇ āĻ•āĻžāϜ āĻ•āϰāĻŦ⧇āĨ¤

🔍 Channels āĻŦāύāĻžāĻŽ Mutex

āĻĒāϰāĻŋāĻ¸ā§āĻĨāĻŋāϤāĻŋāĻŦā§āϝāĻŦāĻšāĻžāϰāĻ•āĻžāϰāĻŖ
Shared variable āϰāĻ•ā§āώāĻž āĻ•āϰāϤ⧇ āĻšāĻŦ⧇MutexāϏāĻšāϜ āĻ“ āĻ•āĻžāĻ°ā§āϝāĻ•āϰ
Data goroutine-āĻāϰ āĻŽāĻ§ā§āϝ⧇ āφāĻĻāĻžāύ-āĻĒā§āϰāĻĻāĻžāύ āĻ•āϰāϤ⧇ āĻšāĻŦ⧇ChannelOwnership āĻ¸ā§āĻĒāĻˇā§āϟ āĻ“ race-free
Pipeline āĻŦāĻž Worker-Pool āĻĄāĻŋāϜāĻžāχāύChannelNatural fit

🧩 āĻ…āύ⧁āĻļā§€āϞāύ

ExerciseāĻ•āĻžāϜ
1ī¸âƒŖā§ĢāϟāĻŋ goroutine āϚāĻžāϞāĻžāĻ“ āĻ“ WaitGroup āĻĻāĻŋā§Ÿā§‡ āĻļ⧇āώ āĻĒāĻ°ā§āϝāĻ¨ā§āϤ āĻ…āĻĒ⧇āĻ•ā§āώāĻž āĻ•āϰāĻžāĻ“
2ī¸âƒŖCounter 1000 āĻŦāĻžāϰ āĻŦ⧃āĻĻā§āϧāĻŋ āĻ•āϰ⧋ Mutex āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰ⧇
3ī¸âƒŖMutex āϏāϰāĻžāĻ“ āĻ“ āĻĻ⧇āĻ–ā§‹ āϕ⧀ āĻšā§Ÿ
4ī¸âƒŖgo run -race main.go āĻĻāĻŋā§Ÿā§‡ race detector āϚāĻžāϞāĻžāĻ“
5ī¸âƒŖRWMutex āĻĻāĻŋā§Ÿā§‡ āĻāĻ• Writer āĻ“ āĻāĻ•āĻžāϧāĻŋāĻ• Reader āϚāĻžāϞāĻžāĻ“

🧭 āϏāĻžāϰāϏāĻ‚āĻ•ā§āώ⧇āĻĒ

āϧāĻžāϰāĻŖāĻžāĻ•āĻžāϜ
WaitGroupāĻāĻ•āĻžāϧāĻŋāĻ• goroutine āĻļ⧇āώ āĻšāĻ“ā§ŸāĻž āĻĒāĻ°ā§āϝāĻ¨ā§āϤ āĻ…āĻĒ⧇āĻ•ā§āώāĻž
MutexShared variable āϕ⧇ āĻāĻ•āϏāĻŽā§Ÿ āĻāĻ• goroutine āĻĒāϰāĻŋāĻŦāĻ°ā§āϤāύ āĻ•āϰāϤ⧇ āĻĻā§‡ā§Ÿ
RWMutexāĻāĻ•āĻžāϧāĻŋāĻ• reader āĻāĻ•āχ āϏāĻžāĻĨ⧇, āĻ•āĻŋāĻ¨ā§āϤ⧁ writer āĻāĻ•āĻž
Race ConditionāĻāĻ•āĻžāϧāĻŋāĻ• goroutine āĻāĻ•āϏāĻžāĻĨ⧇ shared data āĻĒāϰāĻŋāĻŦāĻ°ā§āϤāύ āĻ•āϰāϞ⧇ āĻšā§Ÿ
-race flagRace Condition āĻļāύāĻžāĻ•ā§āϤ āĻ•āϰāĻžāϰ āϟ⧁āϞ
Channel āĻŦāύāĻžāĻŽ MutexChannel data pass āĻ•āϰāϤ⧇, đŸ”ĨMutex data protect āĻ•āϰāϤ⧇ đŸ”Ĩ

Select Statement & Multiplexing

select-āϕ⧇ āφāĻĒāύāĻŋ goroutine-āĻĻ⧇āϰ “āĻŸā§āϰāĻžāĻĢāĻŋāĻ• āĻ•āĻ¨ā§āĻŸā§āϰ⧋āϞāĻžāĻ°â€ āĻŦāϞāϤ⧇ āĻĒāĻžāϰ⧇āύāĨ¤ āĻāϟāĻŋ āĻāĻ•āϟāĻŋ goroutine-āϕ⧇ āĻāĻ•āĻžāϧāĻŋāĻ• āĻšā§āϝāĻžāύ⧇āϞ⧇āϰ āωāĻĒāϰ āĻāĻ•āĻŦāĻžāϰ⧇ āύāϜāϰ āϰāĻžāĻ–āϤ⧇ āϏāĻžāĻšāĻžāĻ¯ā§āϝ āĻ•āϰ⧇ āĻāĻŦāĻ‚ āϝ⧇āϟāĻŋ āφāϗ⧇ āĻĒā§āϰāĻ¸ā§āϤ⧁āϤ (ready) āĻšā§Ÿ, āϏ⧇āϟāĻŋāϰ āωāĻĒāϰ āĻ­āĻŋāĻ¤ā§āϤāĻŋ āĻ•āϰ⧇ āĻ•āĻžāϜ āĻ•āϰ⧇āĨ¤

🧠 āĻ•āύāϏ⧇āĻĒā§āϟ āĻŦā§‹āĻāĻž

🤔 select āϕ⧇āύ āĻĒā§āϰāϝāĻŧā§‹āϜāύ?

āϧāϰ⧁āύ, āφāĻĒāύāĻžāϰ āĻĻ⧁āϟāĻŋ āĻšā§āϝāĻžāύ⧇āϞ āφāϛ⧇, ch1 āĻāĻŦāĻ‚ ch2āĨ¤ āφāĻĒāύāĻŋ āĻĻ⧁āĻŸā§‹ āĻĨ⧇āϕ⧇āχ āĻĄā§‡āϟāĻž āφāĻļāĻž āĻ•āϰāϛ⧇āύāĨ¤ āφāĻĒāύāĻŋ āϝāĻĻāĻŋ āĻāĻ­āĻžāĻŦ⧇ āϞ⧇āϖ⧇āύ:

data1 := <-ch1 // āĻāĻ–āĻžāύ⧇ āφāϟāϕ⧇ āϗ⧇āϞ data2 := <-ch2 // āĻāϟāĻŋ āφāϰ āϚāϞāĻŦ⧇ āύāĻž, āϝāĻĻāĻŋ ch1 āĻ āĻĄā§‡āϟāĻž āύāĻž āφāϏ⧇

āϝāĻĻāĻŋ ch1-āĻ āϕ⧋āύ⧋ āĻĄā§‡āϟāĻž āύāĻž āφāϏ⧇, āĻ•āĻŋāĻ¨ā§āϤ⧁ ch2-āϤ⧇ āĻĄā§‡āϟāĻž āϚāϞ⧇ āφāϏ⧇, āϤāĻŦ⧁āĻ“ āφāĻĒāύāĻžāϰ āĻĒā§āϰ⧋āĻ—ā§āϰāĻžāĻŽ đŸ”Ĩch1-āĻāϰ āϜāĻ¨ā§āϝ āĻ…āύāĻ¨ā§āϤāĻ•āĻžāϞ āĻ…āĻĒ⧇āĻ•ā§āώāĻž (block) āĻ•āϰāϤ⧇ āĻĨāĻžāĻ•āĻŦ⧇āĨ¤đŸ”Ĩ

select āĻāχ āϏāĻŽāĻ¸ā§āϝāĻžāϰ āϏāĻŽāĻžāϧāĻžāύ āĻ•āϰ⧇āĨ¤ āĻāϟāĻŋ āϏāĻŦ āĻšā§āϝāĻžāύ⧇āϞ⧇āϰ āĻĻāĻŋāϕ⧇ “āϤāĻžāĻ•āĻŋā§Ÿā§‡â€ āĻĨāĻžāϕ⧇ āĻāĻŦāĻ‚ āϝ⧇ āĻšā§āϝāĻžāύ⧇āϞāϟāĻŋ āĻĒā§āϰāĻĨāĻŽ āĻĄā§‡āϟāĻž āĻĒāĻžāĻ āĻžāϤ⧇ āĻŦāĻž āĻ—ā§āϰāĻšāĻŖ āĻ•āϰāϤ⧇ āĻĒā§āϰāĻ¸ā§āϤ⧁āϤ āĻšā§Ÿ, āϏ⧇āϟāĻŋāϰ āϕ⧋āĻĄ āĻŦā§āϞāĻ•āϟāĻŋ āϚāĻžāϞāĻžāϝāĻŧāĨ¤


🔹 1. select āϕ⧀ āĻ•āϰ⧇ āĻāĻŦāĻ‚ select āϏāĻŋāύāĻŸā§āϝāĻžāĻ•ā§āϏ

select āĻ…āύ⧇āĻ•āϟāĻž switch āĻāϰ āĻŽāϤ⧋, āĻ•āĻŋāĻ¨ā§āϤ⧁ āĻāϟāĻŋ āĻ•āĻžāϜ āĻ•āϰ⧇ āĻšā§āϝāĻžāύ⧇āϞ āĻ…āĻĒāĻžāϰ⧇āĻļāύ (send/receive) āĻāϰ āωāĻĒāϰāĨ¤

👉 āĻ•āĻžāϜ: āĻāĻ•āĻžāϧāĻŋāĻ• āĻšā§āϝāĻžāύ⧇āϞ⧇āϰ āĻŽāĻ§ā§āϝ⧇ āϝ⧇ āĻšā§āϝāĻžāύ⧇āϞ āφāϗ⧇ ready āĻšā§Ÿ, āϏ⧇āϟāĻžāϰ case execute āĻšā§ŸāĨ¤

📜 select āϏāĻŋāύāĻŸā§āϝāĻžāĻ•ā§āϏ āĻāĻŦāĻ‚ “First Ready Wins”

select-āĻāϰ āϏāĻŋāύāĻŸā§āϝāĻžāĻ•ā§āϏ switch-āĻāϰ āĻŽāϤ⧋āχāĨ¤

select { case data := <-ch1: // ch1 āĻĨ⧇āϕ⧇ āĻĄā§‡āϟāĻž āϰāĻŋāϏāĻŋāĻ­ āĻšāϞ⧇ āĻāϟāĻŋ āϚāϞāĻŦ⧇ fmt.Println("ch1 āĻĨ⧇āϕ⧇ āĻĒ⧇āϞāĻžāĻŽ:", data) case ch2 <- "Hi": // ch2-āϤ⧇ āĻĄā§‡āϟāĻž āϏ⧇āĻ¨ā§āĻĄ āĻ•āϰāĻž āϏāĻŽā§āĻ­āĻŦ āĻšāϞ⧇ āĻāϟāĻŋ āϚāϞāĻŦ⧇ fmt.Println("ch2-āϤ⧇ 'Hi' āĻĒāĻžāĻ āĻžāϞāĻžāĻŽ") case <-ch3: // ch3 āĻĨ⧇āϕ⧇ āĻĄā§‡āϟāĻž āϰāĻŋāϏāĻŋāĻ­ āĻšāϞ⧇ (āĻ•āĻŋāĻ¨ā§āϤ⧁ āĻĄā§‡āϟāĻž āĻŦā§āϝāĻŦāĻšāĻžāϰ āύāĻž āĻ•āϰāϞ⧇) fmt.Println("ch3 āĻĨ⧇āϕ⧇ āϏāĻŋāĻ—āĻ¨ā§āϝāĻžāϞ āĻĒ⧇āϞāĻžāĻŽ") }

đŸšĻ āϚāĻŋāĻ¤ā§āϰ: select āϝ⧇āĻ­āĻžāĻŦ⧇ āĻ•āĻžāϜ āĻ•āϰ⧇

select āĻ¸ā§āĻŸā§‡āϟāĻŽā§‡āĻ¨ā§āϟāϕ⧇ āĻāĻ•āϟāĻŋ āĻĢāĻžāύ⧇āϞ āĻšāĻŋāϏ⧇āĻŦ⧇ āĻ­āĻžāĻŦ⧁āύ:

[goroutine] | <--select--> / | \ / | \ (āĻ…āĻĒ⧇āĻ•ā§āώāĻž) (āĻĒā§āϰāĻ¸ā§āϤ⧁āϤ) (āĻ…āĻĒ⧇āĻ•ā§āώāĻž) | | | [ch1] [ch2] [ch3]
  • select āϤāĻŋāύāϟāĻŋ āĻšā§āϝāĻžāύ⧇āϞ⧇āϰ āĻĻāĻŋāϕ⧇āχ āύāϜāϰ āϰāĻžāϖ⧇āĨ¤
  • āϧāϰ⧁āύ, ch2 āĻĒā§āϰāĻĨāĻŽā§‡ āĻĄā§‡āϟāĻž āϰāĻŋāϏāĻŋāĻ­ āĻ•āϰāĻžāϰ āϜāĻ¨ā§āϝ āĻĒā§āϰāĻ¸ā§āϤ⧁āϤ āĻšāϞ⧋āĨ¤
  • select āĻ…āĻŦāĻŋāϞāĻŽā§āĻŦ⧇ ch2-āĻāϰ case āĻŦā§āϞāĻ•āϟāĻŋ āύāĻŋāĻ°ā§āĻŦāĻžāϚāύ āĻ•āϰāĻŦ⧇ āĻāĻŦāĻ‚ āϚāĻžāϞāĻžāĻŦ⧇āĨ¤ āĻ…āĻ¨ā§āϝ case-āϗ⧁āϞ⧋ āωāĻĒ⧇āĻ•ā§āώāĻž āĻ•āϰāĻž āĻšāĻŦ⧇āĨ¤
  • “First Ready Wins”: āĻāϟāĻŋāχ āĻŽā§‚āϞ āύāĻŋāϝāĻŧāĻŽāĨ¤ āϝ⧇ āĻĒā§āϰāĻĨāĻŽ āĻĒā§āϰāĻ¸ā§āϤ⧁āϤ āĻšāĻŦ⧇, āϏ⧇āχ āϜāĻŋāϤāĻŦ⧇āĨ¤
  • āϝāĻĻāĻŋ āĻāĻ•āĻžāϧāĻŋāĻ• case āĻāĻ•āχ āϏāĻžāĻĨ⧇ āĻĒā§āϰāĻ¸ā§āϤ⧁āϤ āĻĨāĻžāϕ⧇ (āϖ⧁āĻŦ āĻŦāĻŋāϰāϞ, āϤāĻŦ⧇ āϏāĻŽā§āĻ­āĻŦ), select āϤāĻ–āύ āϤāĻžāĻĻ⧇āϰ āĻŽāĻ§ā§āϝ⧇ āĻĨ⧇āϕ⧇ āĻāϞ⧋āĻŽā§‡āϞ⧋āĻ­āĻžāĻŦ⧇ (randomly) āĻāĻ•āϟāĻŋāϕ⧇ āĻŦ⧇āϛ⧇ āύ⧇āϝāĻŧāĨ¤ āĻāϟāĻŋ āϕ⧋āύ⧋ āĻāĻ•āϟāĻŋ āĻšā§āϝāĻžāύ⧇āϞ⧇āϰ āĻĒā§āϰāϤāĻŋ āĻĒāĻ•ā§āώāĻĒāĻžāϤ (bias) āĻĻā§‚āϰ āĻ•āϰāϤ⧇ āϏāĻžāĻšāĻžāĻ¯ā§āϝ āĻ•āϰ⧇āĨ¤

🔹 2. select āĻāϰ āϰ⧁āϞ: “First Ready Wins”

  • āĻāĻ•āĻžāϧāĻŋāĻ• āĻšā§āϝāĻžāύ⧇āϞ ready āĻĨāĻžāĻ•āϞ⧇ → Go āĻ°â€ā§āϝāĻžāĻ¨ā§āĻĄāĻŽāϞāĻŋ āĻāĻ•āϟāĻŋ āĻŦ⧇āϛ⧇ āĻ¨ā§‡ā§ŸāĨ¤
  • āϕ⧋āύ⧋ āĻšā§āϝāĻžāύ⧇āϞ ready āύāĻž āĻĨāĻžāĻ•āϞ⧇ → āĻŦā§āϞāĻ• āĻ•āϰ⧇ (āĻ…āĻĒ⧇āĻ•ā§āώāĻž āĻ•āϰ⧇)āĨ¤
  • āϝāĻĻāĻŋ default āĻĨāĻžāϕ⧇ → āĻŦā§āϞāĻ• āĻ•āϰ⧇ āύāĻžāĨ¤
  • āĻāĻ•āĻŦāĻžāϰ⧇ āĻļ⧁āϧ⧁ āĻāĻ•āϟāĻžāχ case āϚāϞ⧇āĨ¤

🔹 3. 💨 āύāύ-āĻŦā§āϞāĻ•āĻŋāĻ‚ āĻ…āĻĒāĻžāϰ⧇āĻļāύ: default

āϝāĻĻāĻŋ āφāĻĒāύāĻŋ āϚāĻžāύ āϝ⧇ select āϕ⧋āύ⧋ āĻšā§āϝāĻžāύ⧇āϞ⧇āϰ āϜāĻ¨ā§āϝ āĻāĻ•āϟ⧁āĻ“ āĻ…āĻĒ⧇āĻ•ā§āώāĻž āύāĻž āĻ•āϰ⧁āĻ•, āϤāĻŦ⧇ default āϕ⧇āϏ āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰāϤ⧇ āĻĒāĻžāϰ⧇āύāĨ¤

🧩 default āϕ⧇āϏ āϤāĻ–āύāχ āϚāϞ⧇ āϝāĻ–āύ āĻ…āĻ¨ā§āϝ āϕ⧋āύ⧋ case āϤāĻžā§ŽāĻ•ā§āώāĻŖāĻŋāĻ•āĻ­āĻžāĻŦ⧇ āĻĒā§āϰāĻ¸ā§āϤ⧁āϤ āĻĨāĻžāϕ⧇ āύāĻžāĨ¤

select { case data := <-ch: fmt.Println("āĻĄā§‡āϟāĻž āϰāĻŋāϏāĻŋāĻ­āĻĄ:", data) default: // āϕ⧋āύ⧋ āĻšā§āϝāĻžāύ⧇āϞ āϰ⧇āĻĄāĻŋ āύāĻž āĻĨāĻžāĻ•āϞ⧇ āĻāϟāĻŋ āϏāĻžāĻĨ⧇ āϏāĻžāĻĨ⧇ āϚāϞāĻŦ⧇ fmt.Println("āĻāĻ–āύ⧋ āϕ⧋āύ⧋ āĻĄā§‡āϟāĻž āύ⧇āχ, āφāĻŽāĻŋ āĻ…āĻ¨ā§āϝ āĻ•āĻžāϜ āĻ•āϰāĻ›āĻŋ...") }

āĻāϟāĻŋ “try-receive” āĻŦāĻž “non-blocking receive” āĻ•āϰāĻžāϰ āĻāĻ•āϟāĻŋ āϏāĻšāϜ āωāĻĒāĻžāϝāĻŧāĨ¤


🔹 4. âŗ time.After āĻĻāĻŋā§Ÿā§‡ āϟāĻžāχāĻŽāφāωāϟ (Timeout)

āĻ…āύ⧇āĻ• āϏāĻŽā§Ÿ āφāĻŽāϰāĻž āĻ…āύāĻ¨ā§āϤāĻ•āĻžāϞ āĻ…āĻĒ⧇āĻ•ā§āώāĻž āĻ•āϰāϤ⧇ āϚāĻžāχ āύāĻžāĨ¤ āφāĻŽāϰāĻž āĻāĻ•āϟāĻŋ āύāĻŋāĻ°ā§āĻĻāĻŋāĻˇā§āϟ āϏāĻŽāϝāĻŧ āĻĒāĻ°ā§āϝāĻ¨ā§āϤ āĻ…āĻĒ⧇āĻ•ā§āώāĻž āĻ•āϰāϤ⧇ āĻĒāĻžāϰāĻŋāĨ¤ time.After(duration) āĻāĻ•āϟāĻŋ āĻšā§āϝāĻžāύ⧇āϞ āϰāĻŋāϟāĻžāĻ°ā§āύ āĻ•āϰ⧇, āϝāĻž āύāĻŋāĻ°ā§āĻĻāĻŋāĻˇā§āϟ āϏāĻŽāϝāĻŧ (duration) āĻĒāĻžāϰ āĻšāĻ“āϝāĻŧāĻžāϰ āĻĒāϰ āĻāĻ•āϟāĻŋ āϏāĻŋāĻ—āĻ¨ā§āϝāĻžāϞ āĻĒāĻžāĻ āĻžāϝāĻŧāĨ¤

āĻāϟāĻŋ select-āĻāϰ āϏāĻžāĻĨ⧇ āϟāĻžāχāĻŽāφāωāϟ āϤ⧈āϰāĻŋāϰ āϜāĻ¨ā§āϝ āĻĻ⧁āĻ°ā§āĻĻāĻžāĻ¨ā§āϤ:

select { case data := <-longRunningTaskCh: fmt.Println("āĻ•āĻžāϜ āϏāĻŽā§āĻĒāĻ¨ā§āύ:", data) case <-time.After(2 * time.Second): // 2 āϏ⧇āϕ⧇āĻ¨ā§āĻĄ āĻĒāĻžāϰ āĻšāϝāĻŧ⧇ āϗ⧇āϞ⧇ āĻāϟāĻŋ āϚāϞāĻŦ⧇ fmt.Println("āϟāĻžāχāĻŽāφāωāϟ! āφāϰ āĻ…āĻĒ⧇āĻ•ā§āώāĻž āĻ•āϰāĻž āϏāĻŽā§āĻ­āĻŦ āύāĻžāĨ¤") }

āĻāϟāĻŋ āĻāĻ•āϟāĻŋ āĻ•ā§āϞāĻžāϏāĻŋāĻ• āĻĒā§āϝāĻžāϟāĻžāĻ°ā§āύ: āĻšāϝāĻŧ āĻ•āĻžāϜāϟāĻŋ āϏāĻŽā§āĻĒāĻ¨ā§āύ āĻšāĻŦ⧇, āĻ…āĻĨāĻŦāĻž āϟāĻžāχāĻŽāφāωāϟ āĻšāĻŦā§‡â€”āĻĻ⧁āϟāĻŋāϰ āĻŽāĻ§ā§āϝ⧇ đŸ”Ĩāϝ⧇āϟāĻŋ āφāϗ⧇ āϘāϟāĻŦ⧇āĨ¤đŸ”Ĩ


🔀 āĻĒā§āϝāĻžāϟāĻžāĻ°ā§āύ: āĻĢā§āϝāĻžāύ-āχāύ (Fan-In) / āĻŽāĻžāĻ˛ā§āϟāĻŋāĻĒā§āϞ⧇āĻ•ā§āϏāĻŋāĻ‚

āĻĢā§āϝāĻžāύ-āχāύ āĻšāϞ⧋ āĻāĻŽāύ āĻāĻ•āϟāĻŋ āĻĒā§āϝāĻžāϟāĻžāĻ°ā§āύ āϝ⧇āĻ–āĻžāύ⧇ āφāĻĒāύāĻŋ āĻāĻ•āĻžāϧāĻŋāĻ• āχāύāĻĒ⧁āϟ āĻšā§āϝāĻžāύ⧇āϞ āĻĨ⧇āϕ⧇ āĻĄā§‡āϟāĻž āύāĻŋāϝāĻŧ⧇ āĻāĻ•āϟāĻŋāĻŽāĻžāĻ¤ā§āϰ āφāωāϟāĻĒ⧁āϟ āĻšā§āϝāĻžāύ⧇āϞ⧇ āĻāĻ•āĻ¤ā§āϰāĻŋāϤ āĻ•āϰ⧇āύāĨ¤ āĻāϟāĻŋ select-āĻāϰ āĻāĻ•āϟāĻŋ āϖ⧁āĻŦ āϏāĻžāϧāĻžāϰāĻŖ āĻŦā§āϝāĻŦāĻšāĻžāϰāĨ¤

āϧāϰ⧁āύ, āφāĻĒāύāĻžāϰ āĻĻ⧁āϟāĻŋ āϏ⧋āĻ°ā§āϏ (producer) āφāϛ⧇, āϝāĻžāϰāĻž āĻĒā§āϰāĻ¤ā§āϝ⧇āϕ⧇ āύāĻŋāĻœā§‡āĻĻ⧇āϰ āĻšā§āϝāĻžāύ⧇āϞ⧇ āĻĄā§‡āϟāĻž āĻĒāĻžāĻ āĻžāĻšā§āϛ⧇āĨ¤ āφāĻĒāύāĻŋ āϏ⧇āχ āĻĻ⧁āϟāĻŋ āĻšā§āϝāĻžāύ⧇āϞāϕ⧇ “merge” āĻ•āϰ⧇ āĻāĻ•āϟāĻŋ āĻšā§āϝāĻžāύ⧇āϞ⧇ āφāύāϤ⧇ āϚāĻžāύāĨ¤

āφāĻŽāϰāĻž āĻāχ āĻ•āĻžāϜāϟāĻŋ āĻ•āϰāĻžāϰ āϜāĻ¨ā§āϝ āĻāĻ•āϟāĻŋ āĻĢāĻžāĻ‚āĻļāύ āϤ⧈āϰāĻŋ āĻ•āϰāϤ⧇ āĻĒāĻžāϰāĻŋāĨ¤

🔹 Production-grade Fan-In pattern implementation

package main import ( "fmt" "sync" "time" ) // Fan-In: Multiple inputs → Single output func fanIn(inputs ...<-chan string) <-chan string { out := make(chan string) var wg sync.WaitGroup for _, ch := range inputs { wg.Add(1) go func(c <-chan string) { defer wg.Done() for msg := range c { out <- msg } }(ch) } go func() { wg.Wait() close(out) }() return out } func producer(name string, count int) <-chan string { ch := make(chan string) go func() { defer close(ch) for i := 1; i <= count; i++ { ch <- fmt.Sprintf("%s: message %d", name, i) time.Sleep(100 * time.Millisecond) } }() return ch } func main() { ch1 := producer("Service-A", 3) ch2 := producer("Service-B", 3) merged := fanIn(ch1, ch2) for msg := range merged { fmt.Println("Received:", msg) } }

✅ āĻĒā§āϰ⧋āĻĄāĻžāĻ•āĻļāύ Use Cases: Multiple microservices response merge, Log aggregation


🛑 Production Pattern: Graceful Shutdown

package main import ( "fmt" "os" "os/signal" "sync" "syscall" "time" ) func worker(id int, jobs <-chan int, done <-chan struct{}, wg *sync.WaitGroup) { defer wg.Done() for { select { case job, ok := <-jobs: if !ok { fmt.Printf("Worker %d: channel closed\n", id) return } fmt.Printf("Worker %d processing %d\n", id, job) time.Sleep(500 * time.Millisecond) case <-done: fmt.Printf("Worker %d: shutdown signal\n", id) return } } } func main() { jobs := make(chan int, 10) done := make(chan struct{}) var wg sync.WaitGroup for i := 1; i <= 3; i++ { wg.Add(1) go worker(i, jobs, done, &wg) } go func() { for j := 1; j <= 10; j++ { jobs <- j time.Sleep(200 * time.Millisecond) } close(jobs) }() sigChan := make(chan os.Signal, 1) signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM) <-sigChan fmt.Println("\n🛑 Gracefully stopping...") close(done) wg.Wait() fmt.Println("✅ Clean shutdown") }

đŸ’ģ Exercises

⚡ Example 1: Two Channels, First Ready Wins

package main import ( "fmt" "time" ) func main() { ch1 := make(chan string) ch2 := make(chan string) go func() { time.Sleep(1 * time.Second) ch1 <- "🍎 From channel 1" }() go func() { time.Sleep(2 * time.Second) ch2 <- "🍌 From channel 2" }() select { case msg1 := <-ch1: fmt.Println(msg1) case msg2 := <-ch2: fmt.Println(msg2) } }

đŸ’Ŧ āĻĒā§āϰāĻļā§āύ ā§§: **go routine** āĻĨāĻžāĻ•āĻž āϏāĻ¤ā§āĻ¤ā§āĻŦ⧇āĻ“ āφāĻŽāϰāĻž āϕ⧇āύ `WaitGroup` āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰāĻŋāύāĻŋ?

āωāĻ¤ā§āϤāϰ: āĻ•āĻžāϰāĻŖ select āύāĻŋāĻœā§‡āχ channel āĻĨ⧇āϕ⧇ data āĻĒāĻžāĻ“ā§ŸāĻžāϰ āϜāĻ¨ā§āϝ āĻŦā§āϞāĻ• āĻ•āϰ⧇ āĻĨāĻžāϕ⧇āĨ¤ āϝāϤāĻ•ā§āώāĻŖ āύāĻž āϕ⧋āύ⧋ āĻšā§āϝāĻžāύ⧇āϞ ready āĻšā§Ÿ, main goroutine āĻ…āĻĒ⧇āĻ•ā§āώāĻž āĻ•āϰ⧇āĨ¤ āĻ…āĻ°ā§āĻĨāĻžā§Ž main āφāϗ⧇āĻ­āĻžāϗ⧇ terminate āĻšā§Ÿ āύāĻžāĨ¤

WaitGroup āĻĻāϰāĻ•āĻžāϰ āĻšā§Ÿ āϝāĻ–āύ:

  • āϕ⧋āύ⧋ channel āύ⧇āχ, āĻļ⧁āϧ⧁ goroutine āϚāϞāϛ⧇, āĻ…āĻĨāĻŦāĻž
  • āϤ⧁āĻŽāĻŋ manualāĻ­āĻžāĻŦ⧇ goroutine āĻļ⧇āώ āĻšāĻ“ā§ŸāĻž āĻĒāĻ°ā§āϝāĻ¨ā§āϤ āĻ…āĻĒ⧇āĻ•ā§āώāĻž āĻ•āϰāϤ⧇ āϚāĻžāĻ“āĨ¤

🧩 āωāĻĻāĻžāĻšāϰāĻŖ:

// select āύāĻŋāĻœā§‡āχ āĻŦā§āϞāĻ• āĻ•āϰāĻŦ⧇ select { case msg := <-ch: fmt.Println(msg) } // WaitGroup āĻĻāϰāĻ•āĻžāϰ āϝāĻ–āύ āϕ⧋āύ⧋ channel āύ⧇āχ var wg sync.WaitGroup wg.Add(1) go func() { defer wg.Done() fmt.Println("working...") }() wg.Wait()

✅ āϏāĻžāϰāϏāĻ‚āĻ•ā§āώ⧇āĻĒ: āϝāĻ–āύ select āĻŦāĻž <-ch āφāϛ⧇ → main goroutine āύāĻŋāĻœā§‡āχ wait āĻ•āϰ⧇ → WaitGroup āĻĻāϰāĻ•āĻžāϰ āύ⧇āχāĨ¤

đŸ’Ŧ āĻĒā§āϰāĻļā§āύ ⧍: āϕ⧇āύ go routine āϏāϰāĻŋāϝāĻŧ⧇ āĻĻāĻŋāϞ⧇āĻ“ āϕ⧋āĻĄā§‡ “fatal error: all goroutines are asleep - deadlock!” āφāϏ⧇?

āωāĻ¤ā§āϤāϰ: āĻ•āĻžāϰāĻŖ āϕ⧋āύ⧋ goroutine āĻšā§āϝāĻžāύ⧇āϞ⧇ data āĻĒāĻžāĻ āĻžāĻšā§āϛ⧇ āύāĻž, āĻ…āĻĨāϚ select āĻšā§āϝāĻžāύ⧇āϞ āĻĨ⧇āϕ⧇ data receive āĻ•āϰāĻžāϰ āĻšā§‡āĻˇā§āϟāĻž āĻ•āϰāϛ⧇āĨ¤

ch1 := make(chan string) ch2 := make(chan string) select { case msg1 := <-ch1: fmt.Println(msg1) case msg2 := <-ch2: fmt.Println(msg2) }

👉 āĻāĻ–āĻžāύ⧇:

  • ch1 āĻ“ ch2 āĻĨ⧇āϕ⧇ āϕ⧇āω send āĻ•āϰāϛ⧇ āύāĻžāĨ¤
  • select āĻĻ⧁āχāϟāĻŋāϤ⧇āχ receive āĻ…āĻĒ⧇āĻ•ā§āώāĻž āĻ•āϰāϛ⧇āĨ¤
  • āĻĢāϞ⧇ main goroutine āĻŦā§āϞāĻ• āĻšā§Ÿā§‡ āϝāĻžā§Ÿ āĻāĻŦāĻ‚ āĻ…āĻ¨ā§āϝ āϕ⧋āύ⧋ goroutineāĻ“ āϚāϞāϛ⧇ āύāĻžāĨ¤

Go runtime āĻŦ⧁āĻā§‡ āϝāĻžā§Ÿ “āϏāĻŦāĻžāχ āϘ⧁āĻŽāĻŋā§Ÿā§‡ āφāĻ›ā§‡â€ 😴 āϤāĻ–āύ panic āĻĻā§‡ā§Ÿ:

fatal error: all goroutines are asleep - deadlock!

✅ āϏāĻŽāĻžāϧāĻžāύ: āĻāĻ•āϟāĻž goroutine āĻĨ⧇āϕ⧇ data āĻĒāĻžāĻ āĻžāĻ“ āĻŦāĻž buffered channel āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰ⧋āĨ¤

go func() { ch1 <- "🍎 From channel 1" }() go func() { ch2 <- "🍌 From channel 2" }()

āĻ…āĻĨāĻŦāĻž

ch1 := make(chan string, 1) ch1 <- "🍎 From buffered channel" select { case msg := <-ch1: fmt.Println(msg) }

🧠 āϏāĻžāϰāϏāĻ‚āĻ•ā§āώ⧇āĻĒ: Deadlock āĻšā§Ÿ āϝāĻ–āύ āϏāĻŦāĻžāχ āĻ…āĻĒ⧇āĻ•ā§āώāĻž āĻ•āϰ⧇ āĻ•āĻŋāĻ¨ā§āϤ⧁ āϕ⧇āω āĻ•āĻžāϜ āĻ•āϰ⧇ āύāĻžāĨ¤

💡 Deadlock Rule āϏāĻšāϜāĻ­āĻžāĻŦ⧇

āĻ…āĻŦāĻ¸ā§āĻĨāĻžāĻĢāϞāĻžāĻĢāϞ
Channel āĻĨ⧇āϕ⧇ receive āĻšāĻšā§āϛ⧇ āĻ•āĻŋāĻ¨ā§āϤ⧁ āϕ⧇āω send āĻ•āϰāϛ⧇ āύāĻžDeadlock
Channel-āĻ send āĻšāĻšā§āϛ⧇ āĻ•āĻŋāĻ¨ā§āϤ⧁ āϕ⧇āω receive āĻ•āϰāϛ⧇ āύāĻžDeadlock
Unbuffered channel → send & receive āĻāĻ•āϏāĻžāĻĨ⧇ āύāĻž āĻšāϞ⧇Deadlock
Buffered channel → capacity āĻļ⧇āώ āĻšā§Ÿā§‡ āϗ⧇āϞ⧇ send āĻŦā§āϞāĻ• āĻšāĻŦ⧇Possible Deadlock

âš™ī¸ Example 2: Add Default Case (Non-blocking)

select { case msg := <-ch1: fmt.Println("Received:", msg) default: fmt.Println("No channel ready — moving on 🚀") }

⏰ Example 3: Add Timeout

select { case msg := <-ch1: fmt.Println("Got:", msg) case <-time.After(2 * time.Second): fmt.Println("Timeout! ⏰ No data received.") }

🧩 Bonus: Context Cancellation

āϝāĻĻāĻŋ āύāĻŋāĻ°ā§āĻĻāĻŋāĻˇā§āϟ āϏāĻŽā§Ÿ āĻĒāϰ⧇ āϏāĻŦ āĻŦāĻ¨ā§āϧ āĻ•āϰāϤ⧇ āϚāĻžāĻ“ 👇

ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) defer cancel() for { select { case msg := <-out: fmt.Println(msg) case <-ctx.Done(): fmt.Println("🛑 Stopped by context timeout.") return } }

🧾 Mini Cheat Sheet — select Quick Recall

FeatureSyntaxBehavior
Basic selectselect { case msg := <-ch: }Waits for any ready channel
Default casedefault:Prevents blocking
Timeoutcase <-time.After(d):Triggers after duration
Fan-inCombine multiple channelsUse select inside goroutine
Context cancelcase <-ctx.Done():Stop gracefully
Rule“First ready wins”Random if multiple ready

🧠 Summary (āĻŦāĻžāĻ‚āϞāĻžā§Ÿ āϏāĻžāϰāĻžāĻ‚āĻļ)

  • select āĻāĻ•āĻžāϧāĻŋāĻ• āĻšā§āϝāĻžāύ⧇āϞ āĻāĻ•āϏāĻžāĻĨ⧇ āĻŽāύāĻŋāϟāϰ āĻ•āϰ⧇āĨ¤
  • āϝ⧇ āĻšā§āϝāĻžāύ⧇āϞ āφāϗ⧇ ready āĻšā§Ÿ, āϏ⧇āϟāĻžāϰ case āϚāĻžāϞ⧁ āĻšā§ŸāĨ¤
  • default case āĻĻāĻŋāϞ⧇ āĻŦā§āϞāĻ• āĻšā§Ÿ āύāĻžāĨ¤
  • time.After āĻĻāĻŋā§Ÿā§‡ timeout āϏ⧇āϟ āĻ•āϰāĻž āϝāĻžā§ŸāĨ¤
  • Fan-in pattern āĻĻāĻŋā§Ÿā§‡ āĻāĻ•āĻžāϧāĻŋāĻ• channel merge āĻ•āϰāĻž āϝāĻžā§ŸāĨ¤
  • context.WithTimeout āĻĻāĻŋā§Ÿā§‡ clean stop āĻ•āϰāĻž āϝāĻžā§ŸāĨ¤

🏭 Production-Ready Application āĻœā§āĻžāĻžāύ

âš ī¸ Common Production Pitfalls (āĻāĻĄāĻŧāĻŋāϝāĻŧ⧇ āϚāϞ⧁āύ)

1. time.After in Loop (Memory Leak)

// ❌ BAD: Memory leak for { select { case <-ch: // process case <-time.After(1 * time.Second): // āĻĒā§āϰāϤāĻŋāĻŦāĻžāϰ āύāϤ⧁āύ timer // timeout } } // ✅ GOOD: Timer reuse timer := time.NewTimer(1 * time.Second) defer timer.Stop() for { select { case <-ch: timer.Reset(1 * time.Second) case <-timer.C: // timeout } }

2. Goroutine Leak

// ❌ Leak āĻšāĻŦ⧇ func leaky() { ch := make(chan int) go func() { val := <-ch // āϚāĻŋāϰāĻ•āĻžāϞ blocked fmt.Println(val) }() } // channel close āĻšāϝāĻŧāύāĻŋ // ✅ Proper cleanup func proper(done <-chan struct{}) { ch := make(chan int) go func() { select { case val := <-ch: fmt.Println(val) case <-done: return } }() }

đŸ› ī¸ Production Debugging Tools

// Race Detector // go run -race main.go // Goroutine count import "runtime" fmt.Println("Goroutines:", runtime.NumGoroutine()) // pprof for profiling import _ "net/http/pprof" go func() { http.ListenAndServe("localhost:6060", nil) }() // Visit: http://localhost:6060/debug/pprof/

📊 Performance Considerations

āϏāĻŽāĻ¸ā§āϝāĻžāĻĒā§āϰāĻ­āĻžāĻŦāϏāĻŽāĻžāϧāĻžāύ
Unbounded goroutinesMemory exhaustionWorker pool āĻŦā§āϝāĻŦāĻšāĻžāϰ
Large critical sectionHigh contentionSection āϛ⧋āϟ āϰāĻžāϖ⧁āύ
Wrong buffer sizeDeadlock/wasteLoad test āĻ•āϰ⧇ tune āĻ•āϰ⧁āύ
Mutex on hot pathPoor performanceRWMutex āĻŦāĻž lock-free

đŸŽ¯ Decision Matrix: āĻ•āĻ–āύ āϕ⧀ āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰāĻŦ⧇āύ

ScenarioUseāĻ•āĻžāϰāĻŖ
Simple countersync.MutexāϏāĻšāϜ āĻ“ efficient
Read-heavy (90%+)sync.RWMutexConcurrent reads
Data passingChannelOwnership clear
Worker poolChannel + WaitGroupBounded concurrency
Multiple opsselectNon-blocking choice
Graceful stopcontext.ContextClean cancellation
Thread-safe mapsync.MapOptimized

🏆 Production Checklist (Deploy āĻ•āϰāĻžāϰ āφāϗ⧇)

āĻ…āĻŦāĻļā§āϝāχ āĻ•āϰ⧁āύ ✅

  • go test -race ./... āĻĻāĻŋāϝāĻŧ⧇ test āĻ•āϰ⧇āϛ⧇āύ
  • Goroutine leak check āĻ•āϰ⧇āϛ⧇āύ
  • Graceful shutdown implement āĻ•āϰ⧇āϛ⧇āύ
  • Error handling complete
  • Timeout āϏāĻŦ blocking operation-āĻ
  • defer wg.Done() everywhere
  • Sender-āχ channel close āĻ•āϰāϛ⧇
  • Critical section minimal
  • Worker pool pattern (bounded goroutines)
  • Monitoring metrics setup

āĻ•āĻ–āύ⧋ āĻ•āϰāĻŦ⧇āύ āύāĻž ❌

  • Mutex copy (āϏāĻŦāϏāĻŽāϝāĻŧ pointer)
  • Receiver close channel
  • WaitGroup copy
  • time.After in loop
  • Unbounded goroutines
  • Race detector skip
  • Blocking I/O in critical section

📈 Real-World Metrics Monitoring

type AppMetrics struct { GoroutineCount int ActiveWorkers int QueueSize int ProcessedJobs int64 FailedJobs int64 } func monitor() { ticker := time.NewTicker(10 * time.Second) for range ticker.C { m := AppMetrics{ GoroutineCount: runtime.NumGoroutine(), } log.Printf("Metrics: Goroutines=%d", m.GoroutineCount) } }

🎓 āĻĒā§āϰ⧋āĻĄāĻžāĻ•āĻļāύ Tips

  1. āϏāĻŦāϏāĻŽāϝāĻŧ context āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰ⧁āύ - Cancellation āĻ“ timeout-āĻāϰ āϜāĻ¨ā§āϝ
  2. Worker pool size tune āĻ•āϰ⧁āύ - CPU core count āĻ…āύ⧁āϝāĻžāϝāĻŧā§€
  3. Channel buffer size test āĻ•āϰ⧁āύ - Load test āĻĻāĻŋāϝāĻŧ⧇
  4. pprof āĻĻāĻŋāϝāĻŧ⧇ regular profiling āĻ•āϰ⧁āύ - Memory leak āϧāϰāϤ⧇
  5. Graceful shutdown mandatory - Kubernetes/Docker-āĻ
  6. Monitoring setup āĻ•āϰ⧁āύ - Goroutine count, error rate track āĻ•āϰ⧁āύ

📚 āĻāχ Document-āĻ āϝāĻž āĻļāĻŋāĻ–āϞ⧇āύ

✅ WaitGroup - Multiple goroutine coordination
✅ Mutex/RWMutex - Shared data protection
✅ Race Detector - Data race āϖ⧁āρāĻœā§‡ āĻŦ⧇āϰ āĻ•āϰāĻž
✅ Select - Multiple channel operations
✅ Fan-In/Fan-Out - Concurrency patterns
✅ Graceful Shutdown - Clean application stop
✅ Production Patterns - Worker pool, rate limiting
✅ Debugging Tools - pprof, race detector
✅ Best Practices - āϝāĻž āĻ•āϰāϤ⧇ āĻšāĻŦ⧇ āĻāĻŦāĻ‚ āĻāĻĄāĻŧāĻžāϤ⧇ āĻšāĻŦ⧇


🚀 Next Steps

āĻāĻ–āύ āφāĻĒāύāĻŋ āĻĒā§āϰ⧋āĻĄāĻžāĻ•āĻļāύ-ready concurrent Go application āϞāĻŋāĻ–āϤ⧇ āĻĒāĻžāϰāĻŦ⧇āύāĨ¤ āĻĒāϰāĻŦāĻ°ā§āϤ⧀ topics:

  • Context Package (Cancellation, Timeout, Deadline)
  • Advanced Patterns (Circuit breaker, Bulkhead, Retry)
  • Distributed Systems (gRPC, Message queues)
  • Observability (Logging, Tracing, Metrics)

āĻŽāύ⧇ āϰāĻžāĻ–āĻŦ⧇āύ: āĻĒā§āϰ⧋āĻĄāĻžāĻ•āĻļāύ⧇ performance āĻāĻŦāĻ‚ correctness āĻĻ⧁āĻŸā§‹āχ āϗ⧁āϰ⧁āĻ¤ā§āĻŦāĻĒā§‚āĻ°ā§āĻŖāĨ¤ āϏāĻŦāϏāĻŽāϝāĻŧ race detector āĻĻāĻŋāϝāĻŧ⧇ test āĻ•āϰ⧁āύ āĻāĻŦāĻ‚ monitoring setup āĻ•āϰ⧁āύāĨ¤ đŸŽ¯