đ§ 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
| Scenario | Use | āĻāĻžāϰāĻŖ |
|---|---|---|
| Read-heavy (90%+) | sync.Map | Better performance |
| Write-heavy | map + Mutex | Simpler, better for writes |
| Key stable (rarely change) | sync.Map | Optimized for stable keys |
| Known keys at start | map + Mutex | Simpler initialization |
đ 5) 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
| āĻĻāĻŋāĻ | Mutex | RWMutex |
|---|---|---|
| āĻāĻāĻžāϧāĻŋāĻ reader āĻāĻāϏāĻžāĻĨā§ | â āύāĻž | â āĻšā§āϝāĻžāĻ |
| āĻāĻāĻžāϧāĻŋāĻ writer āĻāĻāϏāĻžāĻĨā§ | â āύāĻž | â āύāĻž |
| Read-heavy workload āĻ performance | āϧā§āϰ | āĻĻā§āϰā§āϤ |
| āĻŦā§āϝāĻŦāĻšāĻžāϰ | Shared data āĻāĻŽ āĻĒā§āĻž āĻšā§ | Shared data āĻŦā§āĻļāĻŋ āĻĒā§āĻž āĻšā§ |
â āĻŽāύ⧠āϰāĻžāĻā§
RWMutex āϤāĻāύ āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻāϰā§, āϝāĻāύ read āĻ āύā§āĻ āĻŦā§āĻļāĻŋ āĻāϰ write āĻāĻŽāĨ¤ Write āĻāϞ⧠āϏāĻŦāĻžāĻ āĻ āĻĒā§āĻā§āώāĻž āĻāϰāĻŦā§; Read āĻāϞ⧠āϏāĻŦāĻžāĻ āĻāĻāϏāĻžāĻĨā§ āĻāĻžāĻ āĻāϰāĻŦā§āĨ¤
đ Channels āĻŦāύāĻžāĻŽ Mutex
| āĻĒāϰāĻŋāϏā§āĻĨāĻŋāϤāĻŋ | āĻŦā§āϝāĻŦāĻšāĻžāϰ | āĻāĻžāϰāĻŖ |
|---|---|---|
| Shared variable āϰāĻā§āώāĻž āĻāϰāϤ⧠āĻšāĻŦā§ | Mutex | āϏāĻšāĻ āĻ āĻāĻžāϰā§āϝāĻāϰ |
| Data goroutine-āĻāϰ āĻŽāϧā§āϝ⧠āĻāĻĻāĻžāύ-āĻĒā§āϰāĻĻāĻžāύ āĻāϰāϤ⧠āĻšāĻŦā§ | Channel | Ownership āϏā§āĻĒāώā§āĻ āĻ race-free |
| Pipeline āĻŦāĻž Worker-Pool āĻĄāĻŋāĻāĻžāĻāύ | Channel | Natural 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 āĻļā§āώ āĻšāĻā§āĻž āĻĒāϰā§āϝāύā§āϤ āĻ āĻĒā§āĻā§āώāĻž |
| Mutex | Shared variable āĻā§ āĻāĻāϏāĻŽā§ āĻāĻ goroutine āĻĒāϰāĻŋāĻŦāϰā§āϤāύ āĻāϰāϤ⧠āĻĻā§ā§ |
| RWMutex | āĻāĻāĻžāϧāĻŋāĻ reader āĻāĻāĻ āϏāĻžāĻĨā§, āĻāĻŋāύā§āϤ⧠writer āĻāĻāĻž |
| Race Condition | āĻāĻāĻžāϧāĻŋāĻ goroutine āĻāĻāϏāĻžāĻĨā§ shared data āĻĒāϰāĻŋāĻŦāϰā§āϤāύ āĻāϰāϞ⧠āĻšā§ |
-race flag | Race Condition āĻļāύāĻžāĻā§āϤ āĻāϰāĻžāϰ āĻā§āϞ |
| Channel āĻŦāύāĻžāĻŽ Mutex | Channel 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
| Feature | Syntax | Behavior |
|---|---|---|
| Basic select | select { case msg := <-ch: } | Waits for any ready channel |
| Default case | default: | Prevents blocking |
| Timeout | case <-time.After(d): | Triggers after duration |
| Fan-in | Combine multiple channels | Use select inside goroutine |
| Context cancel | case <-ctx.Done(): | Stop gracefully |
| Rule | âFirst ready winsâ | Random if multiple ready |
đ§ Summary (āĻŦāĻžāĻāϞāĻžā§ āϏāĻžāϰāĻžāĻāĻļ)
selectāĻāĻāĻžāϧāĻŋāĻ āĻā§āϝāĻžāύā§āϞ āĻāĻāϏāĻžāĻĨā§ āĻŽāύāĻŋāĻāϰ āĻāϰā§āĨ¤- āϝ⧠āĻā§āϝāĻžāύā§āϞ āĻāĻā§ ready āĻšā§, āϏā§āĻāĻžāϰ case āĻāĻžāϞ⧠āĻšā§āĨ¤
defaultcase āĻĻāĻŋāϞ⧠āĻŦā§āϞāĻ āĻšā§ āύāĻžāĨ¤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 goroutines | Memory exhaustion | Worker pool āĻŦā§āϝāĻŦāĻšāĻžāϰ |
| Large critical section | High contention | Section āĻā§āĻ āϰāĻžāĻā§āύ |
| Wrong buffer size | Deadlock/waste | Load test āĻāϰ⧠tune āĻāϰā§āύ |
| Mutex on hot path | Poor performance | RWMutex āĻŦāĻž lock-free |
đ¯ Decision Matrix: āĻāĻāύ āĻā§ āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻāϰāĻŦā§āύ
| Scenario | Use | āĻāĻžāϰāĻŖ |
|---|---|---|
| Simple counter | sync.Mutex | āϏāĻšāĻ āĻ efficient |
| Read-heavy (90%+) | sync.RWMutex | Concurrent reads |
| Data passing | Channel | Ownership clear |
| Worker pool | Channel + WaitGroup | Bounded concurrency |
| Multiple ops | select | Non-blocking choice |
| Graceful stop | context.Context | Clean cancellation |
| Thread-safe map | sync.Map | Optimized |
đ 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.Afterin 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
- āϏāĻŦāϏāĻŽāϝāĻŧ context āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻāϰā§āύ - Cancellation āĻ timeout-āĻāϰ āĻāύā§āϝ
- Worker pool size tune āĻāϰā§āύ - CPU core count āĻ āύā§āϝāĻžāϝāĻŧā§
- Channel buffer size test āĻāϰā§āύ - Load test āĻĻāĻŋāϝāĻŧā§
- pprof āĻĻāĻŋāϝāĻŧā§ regular profiling āĻāϰā§āύ - Memory leak āϧāϰāϤā§
- Graceful shutdown mandatory - Kubernetes/Docker-āĻ
- 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 āĻāϰā§āύāĨ¤ đ¯