āļŠāļąāļĄāļ āļēāļĐāļāđāđāļāļāļāļīāļ Go: Goroutine, Channel āđāļĨāļ° Concurrency
āļāļģāļāļēāļĄāļŠāļąāļĄāļ āļēāļĐāļāđāđāļāļāļāļīāļ Go āđāļāļĩāđāļĒāļ§āļāļąāļ goroutine, channel āđāļĨāļ° concurrency pattern āļāđāļēāļāđ āļāļąāļ§āļāļĒāđāļēāļāđāļāđāļ āļāđāļāļāļīāļāļāļĨāļēāļāļāļĩāđāļāļāļāđāļāļĒ āđāļĨāļ°āļāļģāļāļāļāļĢāļ°āļāļąāļāļāļđāđāđāļāļĩāđāļĒāļ§āļāļēāļāļŠāļģāļŦāļĢāļąāļāđāļāļĢāļĩāļĒāļĄāļŠāļąāļĄāļ āļēāļĐāļāđāđāļāļāļāļīāļ Go āļāļĩ 2026

āļāļģāļāļēāļĄāļŠāļąāļĄāļ āļēāļĐāļāđ Go āđāļāļĩāđāļĒāļ§āļāļąāļ goroutine, channel āđāļĨāļ° concurrency āđāļāđāļāļŦāļąāļ§āļāđāļāļāļĩāđāļāđāļēāļāļēāļĒāļāļĩāđāļŠāļļāļāļŠāļģāļŦāļĢāļąāļāļāļđāđāļŠāļĄāļąāļāļĢāļāļĒāđāļēāļāļŠāļĄāđāļģāđāļŠāļĄāļ āļāļ§āļēāļĄāđāļāđāļēāđāļāļāļĒāđāļēāļāļĨāļķāļāļāļķāđāļāđāļāđāļāļ§āļāļīāļāđāļŦāļĨāđāļēāļāļĩāđāļāļ·āļāļŠāļīāđāļāļāļĩāđāđāļĒāļāļ§āļīāļĻāļ§āļāļĢ Go āļĢāļ°āļāļąāļāļāļēāļ§āļļāđāļŠāļāļāļāļāļēāļāļāļđāđāļāļĩāđāļĒāļąāļāđāļĢāļĩāļĒāļāļĢāļđāđāļ āļēāļĐāļēāļāļĒāļđāđ āļāļđāđāļĄāļ·āļāļāļĩāđāļĢāļ§āļāļĢāļ§āļĄāļāļģāļāļēāļĄāļāļĩāđāļāļđāđāļŠāļąāļĄāļ āļēāļĐāļāđāļĄāļąāļāļāļēāļĄāđāļāļāļĩ 2026 āļāļĢāđāļāļĄāļāļąāļ§āļāļĒāđāļēāļāđāļāđāļāļĢāļ°āļāļąāļ production āđāļĨāļ°āđāļŦāļāļļāļāļĨāđāļāļ·āđāļāļāļŦāļĨāļąāļāđāļāđāļĨāļ°āļāļģāļāļāļ
āļāļēāļĢāļŠāļąāļĄāļ āļēāļĐāļāđ concurrency āļāļāļ Go āđāļāđāļāļŠāļēāļĄāļāđāļēāļ: āļāļēāļĢāļāļąāļāļāļēāļĢāļ§āļāļāļĢāļāļĩāļ§āļīāļ goroutine, āļāļ§āļēāļĄāļŦāļĄāļēāļĒāļāļāļ channel (buffered āļāļąāļ unbuffered, āļāļāļīāļ directional) āđāļĨāļ°āļāļēāļĢāļāļĢāļ°āļāļāļ pattern (fan-out/fan-in, worker pool, context cancellation) āļāļēāļĢāļāđāļāļāļāļģ syntax āđāļāļĩāļĒāļāļāļĒāđāļēāļāđāļāļĩāļĒāļ§āđāļĄāđāđāļāļĩāļĒāļāļāļ â āļāļđāđāļŠāļąāļĄāļ āļēāļĐāļāđāļāļēāļāļŦāļ§āļąāļāđāļŦāđāļāļđāđāļŠāļĄāļąāļāļĢāļŠāļēāļĄāļēāļĢāļāļ§āļīāđāļāļĢāļēāļ°āļŦāđ race condition āđāļĨāļ° deadlock āđāļāđ
āļāļ·āđāļāļāļēāļ Goroutine: āļāļģāļāļēāļĄāļāļĩāđāļāļđāļāļāļēāļĄāđāļŠāļĄāļ
āļĢāļāļāđāļĢāļāļāļāļāļāļēāļĢāļŠāļąāļĄāļ āļēāļĐāļāđāļĄāļąāļāļāļāļŠāļāļāļ§āđāļēāļāļđāđāļŠāļĄāļąāļāļĢāđāļāđāļēāđāļ goroutine āļāļĢāļīāļāđ āļŦāļĢāļ·āļāđāļĄāđ â āđāļĄāđāđāļāđāđāļāđāļ§āļīāļāļĩāļŠāļĢāđāļēāļāļĄāļąāļ
āļ: Goroutine āļāļ·āļāļāļ°āđāļĢ āđāļĨāļ°āđāļāļāļāđāļēāļāļāļēāļ OS thread āļāļĒāđāļēāļāđāļĢ?
Goroutine āļāļ·āļāļāļąāļāļāđāļāļąāļ concurrent āļāļĩāđāļĄāļĩāļāđāļģāļŦāļāļąāļāđāļāļē āļāļąāļāļāļēāļĢāđāļāļĒ Go runtime scheduler āđāļĄāđāđāļāđāļĢāļ°āļāļāļāļāļīāļāļąāļāļīāļāļēāļĢ Go runtime āļāļģ multiplexing goroutine āļŦāļĨāļēāļĒāļāļąāļāļāļąāļ§āļĨāļāļāļ OS thread āļāļģāļāļ§āļāļāđāļāļĒāđāļāļĒāđāļāđāđāļĄāđāļāļĨāļāļēāļĢāļāļąāļāļāļēāļĢāļēāļ M:N (M goroutine āļāļđāļāđāļĄāļāļĨāļāļāļ N OS thread)
package main
import (
"fmt"
"runtime"
"sync"
)
func main() {
// Print the number of OS threads available
fmt.Println("GOMAXPROCS:", runtime.GOMAXPROCS(0))
var wg sync.WaitGroup
for i := 0; i < 10000; i++ {
wg.Add(1)
go func(id int) {
defer wg.Done()
// Each goroutine starts with ~2-8KB stack
// OS threads typically start with 1-8MB
_ = id
}(i)
}
wg.Wait()
fmt.Println("All 10,000 goroutines completed")
}āļāļ§āļēāļĄāđāļāļāļāđāļēāļāļŦāļĨāļąāļāļāļĩāđāļāļ§āļĢāļāļĨāđāļēāļ§āļāļķāļāđāļāļāļēāļĢāļŠāļąāļĄāļ āļēāļĐāļāđ: goroutine āđāļĢāļīāđāļĄāļāđāļāļāđāļ§āļĒ stack 2-8KB āļāļĩāđāļāļĒāļēāļĒāđāļāđāđāļāļāđāļāļāļēāļĄāļīāļ āđāļāļĩāļĒāļāļāļąāļ stack āļāļāļāļĩāđ 1-8MB āļāļāļ OS thread āļāļēāļĢāļŠāļĨāļąāļāļāļĢāļīāļāļāļĢāļ°āļĢāļ°āļŦāļ§āđāļēāļ goroutine āļāļąāļāļāļēāļĢāđāļ user space āđāļāļĒ Go scheduler āļŦāļĨāļĩāļāđāļĨāļĩāđāļĒāļāļāļēāļĢāļŠāļĨāļąāļāļāļĢāļīāļāļāļĢāļ°āļĢāļ°āļāļąāļ kernel āļāļĩāđāļĄāļĩāļāđāļāļāļļāļāļŠāļđāļāļāļāļ OS thread āļāļģāđāļŦāđāļāļēāļĢāļŠāļĢāđāļēāļ goroutine 100,000 āļāļąāļ§āđāļāđāļāđāļĢāļ·āđāļāļāļāļāļāļī āđāļāļāļāļ°āļāļĩāđ OS thread 100,000 āļāļąāļ§āļāļ°āđāļāđāļāļĢāļąāļāļĒāļēāļāļĢāļĢāļ°āļāļāļāļāļŦāļĄāļ
āļ: āđāļāļīāļāļāļ°āđāļĢāļāļķāđāļāđāļĄāļ·āđāļ goroutine āđāļāļīāļ panic?
Panic āļāļĩāđāđāļĄāđāļāļđāļ recover āđāļ goroutine āđāļāļāđāļāļēāļĄāļāļ°āļāļģāđāļŦāđāđāļāļĢāđāļāļĢāļĄāļāļąāđāļāļŦāļĄāļāļŦāļĒāļļāļāļāļģāļāļēāļ āļāđāļēāļāļāļēāļ exception āđāļ Java āļŦāļĢāļ·āļ Python āļāļĢāļāļāļĩāđ panic āđāļāļĢāđāļāļķāđāļāļāļēāļĄ call stack āļāļāļ goroutine āļāļąāļ§āđāļāļ â āđāļĄāđāđāļāđ stack āļāļāļ goroutine āļāļĩāđāļŠāļĢāđāļēāļāļĄāļąāļāļāļķāđāļāļĄāļē āļ§āļīāļāļĩāđāļāļĩāļĒāļ§āļāļĩāđāļāļ°āļāļąāļāļāļąāļāđāļāđāļāļ·āļāđāļāđ recover() āļ āļēāļĒāđāļ deferred function āļāļāļ goroutine āđāļāļĩāļĒāļ§āļāļąāļ
package main
import "fmt"
func safeGo(fn func()) {
go func() {
defer func() {
if r := recover(); r != nil {
fmt.Println("recovered from panic:", r)
}
}()
fn() // execute the actual work
}()
}
func main() {
safeGo(func() {
panic("something went wrong")
})
// Give goroutine time to complete
select {}
}āļāļđāđāļŠāļąāļĄāļ āļēāļĐāļāđāļĄāļāļāļŦāļēāļāļ§āļēāļĄāļāļĢāļ°āļŦāļāļąāļāļĢāļđāđāļ§āđāļēāļāļĢāļīāļāļēāļĢ Go āđāļāļŠāļ āļēāļāđāļ§āļāļĨāđāļāļĄ production āļāļ°āļŦāđāļāļŦāļļāđāļĄāļāļēāļĢāđāļĢāļīāđāļĄāļāļģāļāļēāļ goroutine āļāđāļ§āļĒ recovery pattern āđāļĨāļāļĢāļēāļĢāļĩāļāļĒāđāļēāļ errgroup āļāļąāļāļāļēāļĢāđāļĢāļ·āđāļāļāļāļĩāđāđāļāđāļāļĒāđāļēāļāđāļĢāļĩāļĒāļāļĢāđāļāļĒāļāļ§āđāļē
āļāļ§āļēāļĄāļŦāļĄāļēāļĒāļāļāļ Channel: Buffered, Unbuffered āđāļĨāļ° Directional
āļāļģāļāļēāļĄāđāļāļĩāđāļĒāļ§āļāļąāļ channel āđāļāļīāļāđāļāļĒāļ§āđāļēāļāļđāđāļŠāļĄāļąāļāļĢāđāļāđāļēāđāļāđāļĄāđāļāļĨ concurrency āļāļāļ Go āļāļĢāļīāļāđ āļŦāļĢāļ·āļāđāļāđāļāđāļāļāļāļģ pattern
āļ: āļāļ§āļēāļĄāđāļāļāļāđāļēāļāļĢāļ°āļŦāļ§āđāļēāļ buffered āđāļĨāļ° unbuffered channel āļāļ·āļāļāļ°āđāļĢ?
Unbuffered channel (make(chan T)) āļāđāļāļāļāļēāļĢāđāļŦāđāļāļąāđāļāļāļđāđāļŠāđāļāđāļĨāļ°āļāļđāđāļĢāļąāļāļāļĢāđāļāļĄāļāļĢāđāļāļĄāļāļąāļ â āļāļēāļĢāļŠāđāļāļāļ°āļāļĨāđāļāļāļāļāļāļ§āđāļē goroutine āļāļ·āđāļāļāļ°āļĢāļąāļāļāđāļē Buffered channel (make(chan T, n)) āļāļāļļāļāļēāļāđāļŦāđāļŠāđāļāđāļāđāļŠāļđāļāļŠāļļāļ n āļāđāļēāđāļāļĒāđāļĄāđāļāļĨāđāļāļ
package main
import "fmt"
func main() {
// Unbuffered: send blocks until receive is ready
ch := make(chan string)
go func() {
ch <- "hello" // blocks here until main reads
}()
msg := <-ch
fmt.Println(msg)
// Buffered: send does not block until buffer is full
buf := make(chan int, 3)
buf <- 1 // does not block (buffer has space)
buf <- 2 // does not block
buf <- 3 // does not block
// buf <- 4 would block â buffer is full
fmt.Println(<-buf, <-buf, <-buf) // 1 2 3
}āļāļģāļāļēāļĄāļāđāļāđāļāļ·āđāļāļāļāļĩāđāļāļđāđāļŠāļąāļĄāļ āļēāļĐāļāđāļĄāļąāļāļāļēāļĄ: "āđāļĄāļ·āđāļāđāļŦāļĢāđāļāļ§āļĢāđāļĨāļ·āļāļāđāļāđāđāļāļāđāļŦāļ?" Unbuffered channel āļāļąāļāļāļąāļāļāļēāļĢāļāļīāļāđāļāļĢāđāļāļāđ â āļĄāļĩāļāļĢāļ°āđāļĒāļāļāđāđāļĄāļ·āđāļāļāļđāđāļŠāđāļāļāđāļāļāļĢāļđāđāļ§āđāļēāļāļđāđāļĢāļąāļāļāļĢāļ°āļĄāļ§āļĨāļāļĨāļāđāļēāđāļĨāđāļ§ Buffered channel āđāļĒāļāđāļ§āļĨāļēāļāļāļāļāļđāđāļŠāđāļāđāļĨāļ°āļāļđāđāļĢāļąāļāļāļāļāļāļēāļāļāļąāļ â āļĄāļĩāļāļĢāļ°āđāļĒāļāļāđāļŠāļģāļŦāļĢāļąāļ work queue āļŦāļĢāļ·āļ rate limiting āļāļĩāđāļĒāļāļĄāļĢāļąāļāļāļ§āļēāļĄāļŦāļĒāđāļāļāđāļāđāļĢāļ°āļāļąāļāļŦāļāļķāđāļ
āļ: āđāļāļīāļāļāļ°āđāļĢāļāļķāđāļāđāļĄāļ·āđāļāļāļīāļ channel?
āļāļēāļĢāļāļīāļ channel āļŠāđāļāļŠāļąāļāļāļēāļāļ§āđāļēāđāļĄāđāļĄāļĩāļāđāļēāļāļ°āļāļđāļāļŠāđāļāđāļāđāļēāļĄāļēāļāļĩāļ āļāļēāļĢ receive āļāļēāļ channel āļāļĩāđāļāļīāļāđāļĨāđāļ§āļāļ°āļāļ·āļāļāđāļēāļāļąāļāļāļĩāļāļĢāđāļāļĄ zero value āļāļēāļĢāļŠāđāļāļāđāļēāđāļāļĒāļąāļ channel āļāļĩāđāļāļīāļāđāļĨāđāļ§āļāļ°āļāļģāđāļŦāđāđāļāļīāļ panic āļĨāļđāļ range āļāļ channel āļāļ°āļāļāļāđāļāļĒāļāļąāļāđāļāļĄāļąāļāļīāđāļĄāļ·āđāļ channel āļāļđāļāļāļīāļ
package main
import "fmt"
func producer(ch chan<- int, count int) {
for i := 0; i < count; i++ {
ch <- i
}
close(ch) // signal: no more values
}
func main() {
ch := make(chan int, 5)
go producer(ch, 5)
// range exits automatically when channel closes
for val := range ch {
fmt.Println("received:", val)
}
// Reading from closed channel returns zero value + false
val, ok := <-ch
fmt.Printf("after close: val=%d, ok=%v\n", val, ok)
}āļāļļāļāļŠāļģāļāļąāļ: āļĄāļĩāđāļāļĩāļĒāļāļāļđāđāļŠāđāļāđāļāđāļēāļāļąāđāļāļāļĩāđāļāļ§āļĢāļāļīāļ channel āđāļĄāđāđāļāđāļāļđāđāļĢāļąāļ āļāļēāļĢāļāļīāļ channel āļāļĩāđ goroutine āļāļ·āđāļāļĒāļąāļāđāļāļĩāļĒāļāļāļĒāļđāđāļāļ°āļāļģāđāļŦāđāđāļāļīāļ panic
āļāļĢāđāļāļĄāļāļĩāđāļāļ°āļāļīāļāļīāļāļāļēāļĢāļŠāļąāļĄāļ āļēāļĐāļāđ Go āđāļĨāđāļ§āļŦāļĢāļ·āļāļĒāļąāļāļāļĢāļąāļ?
āļāļķāļāļāļāļāđāļ§āļĒāļāļąāļ§āļāļģāļĨāļāļāđāļāļāđāļāđāļāļāļ, flashcards āđāļĨāļ°āđāļāļāļāļāļŠāļāļāđāļāļāļāļīāļāļāļĢāļąāļ
āļāļģāļŠāļąāđāļ Select: āļāļēāļĢ Multiplexing Channel
āļ: select āļāļģāļāļēāļāļāļĒāđāļēāļāđāļĢ āđāļĨāļ°āđāļāļīāļāļāļ°āđāļĢāđāļĄāļ·āđāļāļŦāļĨāļēāļĒ case āļāļĢāđāļāļĄāļāļĢāđāļāļĄāļāļąāļ?
āļāļģāļŠāļąāđāļ select āļāļ°āļāļĨāđāļāļāļāļāļāļ§āđāļēāļŦāļāļķāđāļāđāļāļāļēāļĢāļāļģāđāļāļīāļāļāļēāļĢ channel āļŠāļēāļĄāļēāļĢāļāļāļģāđāļāļīāļāļāļēāļĢāļāđāļāđāļāđ āđāļĄāļ·āđāļāļŦāļĨāļēāļĒ case āļāļĢāđāļāļĄāļāļĢāđāļāļĄāļāļąāļ Go āļāļ°āļŠāļļāđāļĄāđāļĨāļ·āļāļ â āđāļāļ·āđāļāļāđāļāļāļāļąāļāđāļĄāđāđāļŦāđ case āđāļāļāļđāļāļĨāļ°āđāļĨāļĒ
package main
import (
"context"
"fmt"
"time"
)
func fetchFromAPI(ctx context.Context, url string) (string, error) {
resultCh := make(chan string, 1)
errCh := make(chan error, 1)
go func() {
// Simulate API call
time.Sleep(200 * time.Millisecond)
resultCh <- fmt.Sprintf("data from %s", url)
}()
select {
case result := <-resultCh:
return result, nil
case err := <-errCh:
return "", err
case <-ctx.Done():
// Context cancelled or timed out
return "", ctx.Err()
}
}
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)
defer cancel()
result, err := fetchFromAPI(ctx, "https://api.example.com/data")
if err != nil {
fmt.Println("error:", err)
return
}
fmt.Println(result)
}āļāļđāđāļŠāļąāļĄāļ āļēāļĐāļāđāļāļāļŠāļāļāļŠāļāļāļāļĒāđāļēāļāļāđāļ§āļĒ select: āļāļ§āļēāļĄāđāļāđāļēāđāļāļāļāļāļēāļĢāđāļĨāļ·āļāļāđāļāļāļŠāļļāđāļĄ āđāļĨāļ°āļāļ§āļēāļĄāļŠāļēāļĄāļēāļĢāļāđāļāļāļēāļĢāļĢāļ§āļĄ channel āļāļąāļ context.Context āļŠāļģāļŦāļĢāļąāļ pattern timeout āđāļĨāļ°āļāļēāļĢāļĒāļāđāļĨāļīāļ
Concurrency Pattern āļāļĩāđāļāļāļāđāļāļĒāđāļāļāļēāļĢāļŠāļąāļĄāļ āļēāļĐāļāđ
āļāļēāļĢāļŠāļąāļĄāļ āļēāļĐāļāđ Go āļĢāļ°āļāļąāļ senior āđāļāļāļāļļāļāļāļĢāļąāđāļāļĄāļąāļāļĄāļĩāļāļģāļāļēāļĄāđāļāļĩāđāļĒāļ§āļāļąāļāļāļēāļĢāđāļāļĩāļĒāļ pattern āļāđāļāđāļāļāļĩāđāļāļēāļāļĻāļđāļāļĒāđ
Pattern Fan-Out/Fan-In
āļ: āđāļāļĩāļĒāļ pipeline fan-out/fan-in āļāļĩāđāļāļĢāļ°āļĄāļ§āļĨāļāļĨāļĢāļēāļĒāļāļēāļĢāđāļāļāļāļāļēāļāļāļąāļ
Fan-out āļāļĢāļ°āļāļēāļĒāļāļēāļāđāļāļĒāļąāļāļŦāļĨāļēāļĒ goroutine Fan-in āļĢāļ§āļāļĢāļ§āļĄāļāļĨāļĨāļąāļāļāđāļāļēāļāļŦāļĨāļēāļĒ goroutine āđāļāđāļēāļŠāļđāđ channel āđāļāļĩāļĒāļ§
package main
import (
"fmt"
"sync"
)
// generator produces values on a channel
func generator(nums ...int) <-chan int {
out := make(chan int)
go func() {
for _, n := range nums {
out <- n
}
close(out)
}()
return out
}
// square reads from input, squares each value
func square(in <-chan int) <-chan int {
out := make(chan int)
go func() {
for n := range in {
out <- n * n
}
close(out)
}()
return out
}
// fanIn merges multiple channels into one
func fanIn(channels ...<-chan int) <-chan int {
var wg sync.WaitGroup
merged := make(chan int)
for _, ch := range channels {
wg.Add(1)
go func(c <-chan int) {
defer wg.Done()
for val := range c {
merged <- val
}
}(ch)
}
go func() {
wg.Wait()
close(merged) // close after all inputs are drained
}()
return merged
}
func main() {
in := generator(2, 3, 4, 5, 6)
// Fan out: two goroutines reading from same channel
c1 := square(in)
c2 := square(in)
// Fan in: merge results
for result := range fanIn(c1, c2) {
fmt.Println(result)
}
}Insight āļŦāļĨāļąāļāļāļĩāđāļāļđāđāļŠāļąāļĄāļ āļēāļĐāļāđāļāđāļāļāļāļēāļĢ: channel generator āļāļđāļāđāļāļĢāđāļĢāļ°āļŦāļ§āđāļēāļ c1 āđāļĨāļ° c2 āļāļąāļāļāļąāđāļāđāļāđāļĨāļ°āļāđāļēāļāļ°āļāļđāļāļāļĢāļ°āļĄāļ§āļĨāļāļĨāđāļāļĒ worker āđāļāļĩāļĒāļāļāļąāļ§āđāļāļĩāļĒāļ§ (āđāļĄāđāļāđāļģāļāļąāļ) āļāļąāļāļāđāļāļąāļ fanIn āđāļāđ WaitGroup āđāļāļ·āđāļāļāļĢāļēāļāļ§āđāļē input channel āļāļąāđāļāļŦāļĄāļāļāļđāļāļāđāļēāļāļāļāļŦāļĄāļāļāđāļāļāļāļīāļ channel āļĢāļ§āļĄ
Worker Pool āļāđāļ§āļĒ errgroup
āļ: āđāļāļĩāļĒāļ bounded worker pool āļāļĩāđāļĄāļĩāļāļēāļĢāļāļąāļāļāļēāļĢ error āļāļĒāđāļēāļāđāļĢ?
Package golang.org/x/sync/errgroup (āļŠāđāļ§āļāļŦāļāļķāđāļāļāļāļ Go extended standard library) āđāļāđāļāļąāļāļŦāļēāļāļĩāđāđāļāđāļāļĒāđāļēāļāđāļĢāļĩāļĒāļāļĢāđāļāļĒ āđāļāļĒāļāļąāļāļāļēāļĢāļ§āļāļāļĢāļāļĩāļ§āļīāļ goroutine āļĢāļ§āļāļĢāļ§āļĄ error āđāļĢāļ āđāļĨāļ°āļāļģāļāļēāļāļĢāđāļ§āļĄāļāļąāļ context āļŠāļģāļŦāļĢāļąāļāļāļēāļĢāļĒāļāđāļĨāļīāļ
package main
import (
"context"
"fmt"
"golang.org/x/sync/errgroup"
)
func processItem(ctx context.Context, id int) error {
// Check for cancellation before heavy work
select {
case <-ctx.Done():
return ctx.Err()
default:
}
if id == 7 {
return fmt.Errorf("failed to process item %d", id)
}
fmt.Printf("processed item %d\n", id)
return nil
}
func main() {
g, ctx := errgroup.WithContext(context.Background())
g.SetLimit(3) // maximum 3 concurrent goroutines
for i := 0; i < 10; i++ {
id := i
g.Go(func() error {
return processItem(ctx, id)
})
}
// Wait blocks until all goroutines finish
// Returns the first non-nil error
if err := g.Wait(); err != nil {
fmt.Println("pipeline error:", err)
}
}āļāļąāđāļāđāļāđ Go 1.24 (āđāļ§āļāļĢāđāļāļąāļ stable āļĨāđāļēāļŠāļļāļ āļ āļāđāļāļāļĩ 2026) pattern āļāļĩāđāļĒāļąāļāļāļāđāļāđāļāļ§āļīāļāļĩāļāļĩāđāđāļāļ°āļāļģ āđāļĄāļāļāļ SetLimit āļāļđāļāđāļāļīāđāļĄāđāļ Go 1.20 āđāļĨāļ°āļāļģāđāļŦāđāđāļĄāđāļāđāļāļāđāļāļĩāļĒāļāļāļēāļĢāļāļģāļāļąāļ concurrency āđāļāļ semaphore āļāđāļ§āļĒāļāļąāļ§āđāļāļāļāļĩāļāļāđāļāđāļ
Race Condition āđāļĨāļ° sync Primitive
āļ: āļāļĢāļ§āļāļāļąāļāđāļĨāļ°āļāđāļāļāļāļąāļ race condition āđāļ Go āļāļĒāđāļēāļāđāļĢ?
Go āļĄāļĩ race detector āđāļāļāļąāļ§āļāļĩāđāđāļāļīāļāđāļāđāļāļēāļāļāđāļ§āļĒ flag -race āđāļāļĢāļ·āđāļāļāļĄāļ·āļāļāļĩāđāļāļĢāļ§āļāļāļąāļāļāļēāļĢāđāļāđāļēāļāļķāļ shared memory āđāļāļ concurrent āļāļĩāđāđāļĄāđāļāļīāļāđāļāļĢāđāļāļāđāļāļąāļāđāļāļāļāļ° runtime
package main
import (
"fmt"
"sync"
"sync/atomic"
)
// BAD: race condition â do not use in production
func unsafeCounter() int {
counter := 0
var wg sync.WaitGroup
for i := 0; i < 1000; i++ {
wg.Add(1)
go func() {
defer wg.Done()
counter++ // DATA RACE: concurrent read/write
}()
}
wg.Wait()
return counter // result is non-deterministic
}
// GOOD: atomic operations for simple counters
func atomicCounter() int64 {
var counter atomic.Int64
var wg sync.WaitGroup
for i := 0; i < 1000; i++ {
wg.Add(1)
go func() {
defer wg.Done()
counter.Add(1) // thread-safe atomic increment
}()
}
wg.Wait()
return counter.Load() // always 1000
}
// GOOD: mutex for complex shared state
type SafeMap struct {
mu sync.RWMutex
data map[string]int
}
func (m *SafeMap) Set(key string, val int) {
m.mu.Lock() // exclusive lock for writes
defer m.mu.Unlock()
m.data[key] = val
}
func (m *SafeMap) Get(key string) (int, bool) {
m.mu.RLock() // shared lock for reads
defer m.mu.RUnlock()
v, ok := m.data[key]
return v, ok
}
func main() {
fmt.Println("unsafe:", unsafeCounter()) // unpredictable
fmt.Println("atomic:", atomicCounter()) // always 1000
}āļāļģāļāļāļāļŠāļąāļĄāļ āļēāļĐāļāđāļāļ§āļĢāļāļĢāļāļāļāļĨāļļāļĄāļŠāļēāļĄāļāļĨāļĒāļļāļāļāđāļāļēāļĢāļāļīāļāđāļāļĢāđāļāļāđ: sync.Mutex / sync.RWMutex āļŠāļģāļŦāļĢāļąāļ shared state āļāļĩāđāļāļąāļāļāđāļāļ, sync/atomic āļŠāļģāļŦāļĢāļąāļ counter āđāļĨāļ° flag āļāļģāļāļ§āļāđāļĨāđāļ, āđāļĨāļ° channel āļŠāļģāļŦāļĢāļąāļāļāļēāļĢāļŠāļ·āđāļāļŠāļēāļĢāļĢāļ°āļŦāļ§āđāļēāļ goroutine ("share memory by communicating, donât communicate by sharing memory") āļāļēāļĢāļĢāļąāļ go test -race ./... āļāļ§āļĢāđāļāđāļāļŠāđāļ§āļāļŦāļāļķāđāļāļāļāļāļāļļāļ CI pipeline
Context āđāļĨāļ° Pattern āļāļēāļĢāļĒāļāđāļĨāļīāļ
āļ: āļāļāļīāļāļēāļĒāļ§āđāļē context.Context āļāļ§āļāļāļļāļĄāļ§āļāļāļĢāļāļĩāļ§āļīāļ goroutine āļāļĒāđāļēāļāđāļĢ
Package context āļĄāļĩāļāļĨāđāļāļŠāļģāļŦāļĢāļąāļāļŠāđāļāļāđāļāļŠāļąāļāļāļēāļāļāļēāļĢāļĒāļāđāļĨāļīāļ deadline āđāļĨāļ°āļāđāļēāļāđāļēāļāđ āđāļāļĢāļ°āļāļąāļ request āļāđāļēāļĄāļāļāļāđāļāļāļāļāļ goroutine āļāļļāļ goroutine āļāļĩāđāļāļģāļāļēāļāļĒāļēāļ§āļāļēāļāļāļ§āļĢāļĢāļąāļ context.Context āđāļāđāļāļāļēāļĢāļēāļĄāļīāđāļāļāļĢāđāļāļąāļ§āđāļĢāļ
package main
import (
"context"
"fmt"
"time"
)
// worker simulates a long-running task
func worker(ctx context.Context, id int, results chan<- string) {
select {
case <-time.After(time.Duration(id*100) * time.Millisecond):
results <- fmt.Sprintf("worker %d: done", id)
case <-ctx.Done():
results <- fmt.Sprintf("worker %d: cancelled (%v)", id, ctx.Err())
}
}
func main() {
// Parent context with 250ms deadline
ctx, cancel := context.WithTimeout(context.Background(), 250*time.Millisecond)
defer cancel()
results := make(chan string, 5)
// Launch 5 workers with increasing durations
for i := 1; i <= 5; i++ {
go worker(ctx, i, results)
}
// Collect all results
for i := 0; i < 5; i++ {
fmt.Println(<-results)
}
}Worker 1 āđāļĨāļ° 2 āļāļģāļāļēāļāđāļŠāļĢāđāļāļ āļēāļĒāđāļ deadline 250ms Worker 3, 4 āđāļĨāļ° 5 āđāļāđāļĢāļąāļāļŠāļąāļāļāļēāļāļĒāļāđāļĨāļīāļāļāđāļēāļ ctx.Done() Pattern āļāļĩāđāđāļāđāļāļāļ·āđāļāļāļēāļāļŠāļģāļŦāļĢāļąāļāļāļēāļĢāļŠāļĢāđāļēāļ HTTP server āđāļĨāļ° microservice āļāļĩāđāļĄāļĩāļāļ§āļēāļĄāļāļāļāļēāļāđāļ Go â āļāļļāļ request handler āļāļ°āļĢāļąāļ context āļāļĩāđāļŠāđāļāļāđāļāļŠāļąāļāļāļēāļāļĒāļāđāļĨāļīāļāđāļĄāļ·āđāļ client āļāļąāļāļāļēāļĢāđāļāļ·āđāļāļĄāļāđāļ
āļŦāđāļēāļĄāđāļāđāļ context.Context āđāļ§āđāđāļ field āļāļāļ struct āđāļāļāļŠāļēāļĢāļāđāļēāļāļāļīāļāļāļāļ Go āļĢāļ°āļāļļāļāļąāļāđāļāļāļ§āđāļē: "Do not store Contexts inside a struct type; instead, pass a Context explicitly to each function that needs it." āļāļđāđāļŠāļąāļĄāļ āļēāļĐāļāđāļāļāļŠāļāļāđāļĢāļ·āđāļāļāļāļĩāđāđāļāļ·āđāļāļāļĢāļ°āđāļĄāļīāļāļ§āđāļēāļāļđāđāļŠāļĄāļąāļāļĢāļāļāļīāļāļąāļāļīāļāļēāļĄāđāļāļ§āļāļēāļāļāļāļ Go āļŦāļĢāļ·āļāđāļĄāđ
āļāļēāļĢāļāļĢāļ§āļāļāļąāļ Deadlock: āļāļģāļāļēāļĄāļŠāļąāļĄāļ āļēāļĐāļāđāđāļāļāļŦāļĨāļāļāļĨāđāļ
āļ: āđāļāđāļāļāļĩāđāļāļ°āđāļāļīāļ deadlock āļŦāļĢāļ·āļāđāļĄāđ? āđāļāļĢāļēāļ°āļāļ°āđāļĢ?
āļāļģāļāļēāļĄāđāļĢāļ·āđāļāļ deadlock āđāļāđāļāļāļĩāđāļāļīāļĒāļĄāđāļāļĢāļēāļ°āļāļāļŠāļāļāļāļ§āļēāļĄāļŠāļēāļĄāļēāļĢāļāđāļāļāļēāļĢāļ§āļīāđāļāļĢāļēāļ°āļŦāđāļāļēāļĢāļāļąāļāļāļēāļĢāļēāļ goroutine āđāļĨāļ°āļāļēāļĢāļāļģāđāļāļīāļāļāļēāļĢ channel
package main
func main() {
ch := make(chan int)
ch <- 42 // DEADLOCK: unbuffered send with no receiver
// The main goroutine blocks here forever
// Go runtime detects this: "fatal error: all goroutines are asleep"
}āļ§āļīāļāļĩāđāļāđāđāļāļāļĢāļāđāļāļāļĢāļāļĄāļē: āļāļģ channel āđāļāđāļāđāļāļ buffered (make(chan int, 1)) āļŦāļĢāļ·āļāļŠāļĢāđāļēāļ goroutine āđāļāļ·āđāļāļĢāļąāļāļāđāļāļāļŠāđāļ Go runtime āļāļĢāļ§āļāļāļąāļ deadlock āđāļĄāļ·āđāļāļāļļāļ goroutine āļāļđāļāļāļĨāđāļāļ â āđāļāđāđāļāļāļēāļ°āđāļĄāļ·āđāļ āļāļļāļāļāļąāļ§ āļŦāļĒāļļāļāļāļģāļāļēāļ āļŦāļēāļāļĄāļĩāđāļĄāđāđāļāđ goroutine āđāļāļĩāļĒāļ§āļāļĩāđāļĒāļąāļāļāļģāļāļēāļāļāļĒāļđāđ (āđāļāđāļ HTTP server āļāļĩāđāļāļģāļāļēāļāļāļĒāļđāđāđāļāļ·āđāļāļāļŦāļĨāļąāļ) runtime āļāļ°āđāļĄāđāļāļĢāļ§āļāļāļąāļ deadlock āļāļēāļāļŠāđāļ§āļ
Go runtime āļāļĢāļ§āļāļāļąāļ deadlock āđāļāđāđāļāļāļēāļ°āđāļĄāļ·āđāļāļāļļāļ goroutine āđāļāđāļāļĢāđāļāļĢāļĄāļāļđāļāļāļĨāđāļāļ āđāļāđāļāļāļāļĨāļīāđāļāļāļąāļāļāļĢāļīāļāļāļĩāđāļĄāļĩ HTTP server āļŦāļĢāļ·āļ background worker goroutine āļāļĩāđāļĢāļąāđāļ§āđāļŦāļĨāđāļĨāļ°āđāļāļīāļ deadlock āļāļ°āđāļĄāđāļāļĢāļ°āļāļļāđāļāļāļąāļ§āļāļĢāļ§āļāļāļąāļāļāļāļ runtime āđāļāļĢāļ·āđāļāļāļĄāļ·āļāļāļĒāđāļēāļ pprof āđāļĨāļ° goroutine dump (runtime.Stack) āļāļģāđāļāđāļāļŠāļģāļŦāļĢāļąāļāļ§āļīāđāļāļĢāļēāļ°āļŦāđāļāļąāļāļŦāļēāđāļŦāļĨāđāļēāļāļĩāđāđāļāļŠāļ āļēāļāđāļ§āļāļĨāđāļāļĄ production
Pattern āļāļąāđāļāļŠāļđāļ: āļāļēāļĢāļāļĢāļ°āļĄāļ§āļĨāļāļĨāđāļāļ Concurrent āļāļĢāđāļāļĄ Rate Limiting
āļ: āđāļāļĩāļĒāļ rate-limited concurrent API call āļāļĒāđāļēāļāđāļĢ?
āļāļģāļāļēāļĄāļāļĩāđāļāļāļŠāļāļāļāļ§āļēāļĄāļŠāļēāļĄāļēāļĢāļāđāļāļāļēāļĢāļĢāļ§āļĄ concurrency primitive āļŦāļĨāļēāļĒāļāļąāļ§āđāļāđāļēāđāļāđāļāđāļāļĨāļđāļāļąāļāļāļĩāđāļŠāļĄāļāļđāļĢāļāđ
package main
import (
"context"
"fmt"
"sync"
"time"
)
// RateLimiter controls concurrent and temporal access
type RateLimiter struct {
semaphore chan struct{} // limits concurrency
ticker *time.Ticker // limits rate
}
func NewRateLimiter(maxConcurrent int, interval time.Duration) *RateLimiter {
return &RateLimiter{
semaphore: make(chan struct{}, maxConcurrent),
ticker: time.NewTicker(interval),
}
}
func (rl *RateLimiter) Execute(ctx context.Context, fn func() error) error {
// Wait for rate limit tick
select {
case <-rl.ticker.C:
case <-ctx.Done():
return ctx.Err()
}
// Acquire concurrency slot
select {
case rl.semaphore <- struct{}{}:
case <-ctx.Done():
return ctx.Err()
}
defer func() { <-rl.semaphore }() // release slot
return fn()
}
func main() {
rl := NewRateLimiter(3, 100*time.Millisecond)
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
wg.Add(1)
go func(id int) {
defer wg.Done()
err := rl.Execute(ctx, func() error {
fmt.Printf("[%v] processing %d\n", time.Now().Format("04:05.000"), id)
time.Sleep(150 * time.Millisecond) // simulate work
return nil
})
if err != nil {
fmt.Printf("item %d: %v\n", id, err)
}
}(i)
}
wg.Wait()
}Pattern āļāļĩāđāļĢāļ§āļĄ semaphore āļāļĩāđāđāļāđ channel (āļāļģāļāļąāļ concurrency) āļāļąāļ ticker (āļāļģāļāļąāļāļāļąāļāļĢāļē) Double select āļāļĩāđāļāļĢāļ§āļāļŠāļāļ context āļĢāļąāļāļāļĢāļ°āļāļąāļāļāļēāļĢ shutdown āđāļāļ graceful āļāļģāļāļāļāļĢāļ°āļāļąāļ production āđāļāđāļāļāļĩāđāļāļ·āļāļŠāļīāđāļāļāļĩāđāđāļĒāļāļāļđāđāļŠāļĄāļąāļāļĢāļĢāļ°āļāļąāļ senior āļāļāļāļāļēāļāļāļāļāļ·āđāļ
āđāļĢāļīāđāļĄāļāļķāļāļāđāļāļĄāđāļĨāļĒ!
āļāļāļŠāļāļāļāļ§āļēāļĄāļĢāļđāđāļāļāļāļāļļāļāļāđāļ§āļĒāļāļąāļ§āļāļģāļĨāļāļāļŠāļąāļĄāļ āļēāļĐāļāđāđāļĨāļ°āđāļāļāļāļāļŠāļāļāđāļāļāļāļīāļāļāļĢāļąāļ
āļŠāļĢāļļāļ
- Goroutine āđāļāđāļ thread āļĢāļ°āļāļąāļ user-space āļāļĩāđ Go runtime āļāļąāļāļāļēāļĢāļāđāļ§āļĒāļāļēāļĢāļĨāļģāļāļąāļ M:N; āļāđāļāļ recover panic āđāļ goroutine āļāļĩāđāļāļđāļ spawn āđāļŠāļĄāļ
- Unbuffered channel āļāļīāļāđāļāļĢāđāļāļāđāļāļđāđāļŠāđāļāđāļĨāļ°āļāļđāđāļĢāļąāļ; buffered channel āđāļĒāļāđāļ§āļĨāļē â āđāļĨāļ·āļāļāļāļēāļĄāļ§āđāļēāļāļđāđāļŠāđāļāļāđāļāļāļāļēāļĢāļāļēāļĢāļĒāļ·āļāļĒāļąāļāļŦāļĢāļ·āļāđāļĄāđ
- āļāļģāļŠāļąāđāļ
selectāļāļģ multiplexing āļāļēāļĢāļāļģāđāļāļīāļāļāļēāļĢ channel āļāđāļ§āļĒāļāļēāļĢāđāļĨāļ·āļāļāļŠāļļāđāļĄāđāļĄāļ·āđāļāļŦāļĨāļēāļĒ case āļāļĢāđāļāļĄ; āđāļāđāļĢāđāļ§āļĄāļāļąāļcontext.ContextāļŠāļģāļŦāļĢāļąāļ timeout - Fan-out/fan-in āđāļĨāļ° worker pool (āļāđāļēāļ
errgroup.SetLimit) āđāļāđāļāļŠāļāļ concurrency pattern āļāļĩāđāļāļđāļāļāļēāļĄāļāđāļāļĒāļāļĩāđāļŠāļļāļ - āđāļāđ
sync.MutexāļŠāļģāļŦāļĢāļąāļ shared state āļāļąāļāļāđāļāļ,sync/atomicāļŠāļģāļŦāļĢāļąāļ counter āļāđāļēāļĒāđ, āđāļĨāļ° channel āļŠāļģāļŦāļĢāļąāļāļāļēāļĢāļŠāļ·āđāļāļŠāļēāļĢāļĢāļ°āļŦāļ§āđāļēāļ goroutine - āļĢāļąāļ
go test -raceāđāļ CI āđāļŠāļĄāļāđāļāļ·āđāļāļāļąāļ data race; deadlock āļāļēāļāļŠāđāļ§āļāļāđāļāļāđāļāđpprofāđāļāļāļēāļĢāļ§āļīāđāļāļĢāļēāļ°āļŦāđ - āļŦāđāļēāļĄāđāļāđāļ
context.Contextāđāļ struct â āļŠāđāļāđāļāđāļāļāļēāļĢāļēāļĄāļīāđāļāļāļĢāđāļāļąāļ§āđāļĢāļāļāļāļāļāļąāļāļāđāļāļąāļ - Rate limiting āđāļ Go āļĢāļ§āļĄ semaphore channel āļāļąāļ ticker āļŦāđāļāļŦāļļāđāļĄāđāļ select statement āļāļĩāđāļĢāļąāļāļĢāļđāđ context
āđāļĢāļīāđāļĄāļāļķāļāļāđāļāļĄāđāļĨāļĒ!
āļāļāļŠāļāļāļāļ§āļēāļĄāļĢāļđāđāļāļāļāļāļļāļāļāđāļ§āļĒāļāļąāļ§āļāļģāļĨāļāļāļŠāļąāļĄāļ āļēāļĐāļāđāđāļĨāļ°āđāļāļāļāļāļŠāļāļāđāļāļāļāļīāļāļāļĢāļąāļ

āđāļāļĩāļĒāļāđāļāļĒ
Anthony Fillion-MailletāļāļąāļāļāļąāļāļāļēāļāļđāļĨāļŠāđāļāļ āļāļđāđāļāđāļāļāļąāđāļ SharpSkill
āđāļāđāļāļāļąāļāļāļąāļāļāļēāļāļđāļĨāļŠāđāļāļāļĄāļēāļāļ§āđāļē 10 āļāļĩ āļāļđāđāļĨ SharpSkill āđāļĨāļ°āļĢāļąāļāļāļīāļāļāļāļāļāļļāļāļŠāļīāđāļāļāļĩāđāđāļāļĒāđāļāļĢāđāļāļĩāđāļāļĩāđ
āļāļąāļāđāļāļāđāļĄāļ·āđāļ 1 āđāļĄāļĐāļēāļĒāļ 2569
āđāļāđāļ
āđāļāļĢāđ
āļāļāļāļ§āļēāļĄāļāļĩāđāđāļāļĩāđāļĒāļ§āļāđāļāļ

āļāļēāļĢāļāļģāļāļēāļāļāļĢāđāļāļĄāļāļąāļāđāļ Go: Goroutines āđāļĨāļ° Channels - āļāļđāđāļĄāļ·āļāļāļāļąāļāļŠāļĄāļāļđāļĢāļāđ
āđāļāļĩāđāļĒāļ§āļāļēāļāļāļēāļĢāļāļģāļāļēāļāļāļĢāđāļāļĄāļāļąāļāđāļ Go āļāđāļ§āļĒ goroutines āđāļĨāļ° channels āļĢāļđāļāđāļāļāļāļąāđāļāļŠāļđāļ āļāļēāļĢāļāļīāļāđāļāļĢāđāļāļāđ āļāļģāļŠāļąāđāļ select āđāļĨāļ°āđāļāļ§āļāļēāļāļāļāļīāļāļąāļāļīāļāļĩāđāļāļĩāļāļĩāđāļŠāļļāļāļāļĢāđāļāļĄāļāļąāļ§āļāļĒāđāļēāļāđāļāđāļāđāļāļĒāļĨāļ°āđāļāļĩāļĒāļ

25 āļāļģāļāļēāļĄāļŠāļąāļĄāļ āļēāļĐāļāđ Go āļĒāļāļāļāļīāļĒāļĄ: āļāļđāđāļĄāļ·āļāļāļāļąāļāļŠāļĄāļāļđāļĢāļāđāļŠāļģāļŦāļĢāļąāļāļāļąāļāļāļąāļāļāļē
āļāļīāļāļīāļāļāļēāļĢāļŠāļąāļĄāļ āļēāļĐāļāđ Go āļāđāļ§āļĒ 25 āļāļģāļāļēāļĄāļāļĩāđāļāļđāļāļāļēāļĄāļāđāļāļĒ goroutine, channel, interface āđāļĨāļ°āļĢāļđāļāđāļāļāļāļēāļĢāļāļģāļāļēāļāļāļĢāđāļāļĄāļāļąāļāļāļĢāđāļāļĄāļāļąāļ§āļāļĒāđāļēāļāđāļāđāļ

Go: āļāļ·āđāļāļāļēāļāļŠāļģāļŦāļĢāļąāļāļāļąāļāļāļąāļāļāļē Java/Python āđāļāļāļĩ 2026
āđāļĢāļĩāļĒāļāļĢāļđāđ Go āļāļĒāđāļēāļāļĢāļ§āļāđāļĢāđāļ§āđāļāļĒāđāļāđāļāļĢāļ°āļŠāļāļāļēāļĢāļāđāļāļēāļ Java āļŦāļĢāļ·āļ Python Goroutine, channel, interface āđāļĨāļ° pattern āļŠāļģāļāļąāļāļŠāļģāļŦāļĢāļąāļāļāļēāļĢāđāļāļĨāļĩāđāļĒāļāļāđāļēāļāļāļĒāđāļēāļāļĢāļēāļāļĢāļ·āđāļ