A goroutine lets a function run concurrently with its caller. Starting one is easy; deciding when it finishes, what variables it reads, and whether it improves performance takes more care. section 1.2 section 9.2
A common mistake is to start a goroutine without an exit plan. A goroutine receiving in `for v := range ch` exits when `ch` closes. If the function supplying `ch` never closes it, the goroutine remains. A leaked goroutine can retain heap references and resources such as connections or open files. section 9.2
Choose an exit mechanism that matches who owns the work:
- For a watcher whose work should end on cancellation, pass it a context and have its watch operation close its resources when that context is canceled. section 9.2
- If the caller owns a watcher, return it with a close operation and arrange for the caller to invoke that operation. section 9.2
A closure reads a changing loop variable
Consider `for _, i := range s { go func() { fmt.Print(i) }() }` for `s := []int{1, 2, 3}`. In this example, the closures refer to the same `i`; they do not preserve its value when each goroutine starts. The output can therefore differ from the expected values, and completion order is not guaranteed. Give each invocation its value instead: `for _, i := range s { go func(val int) { fmt.Print(val) }(i) }`. This fixes which value each function prints, not the order of printing. section 9.3
Concurrency also needs coordination. In parallel merge sort, two goroutines sort separate halves of a slice; the parent calls `Wait` on a `sync.WaitGroup` before merging them. Call `Add` before starting the goroutines so the group accounts for the work they will perform. section 8.2.2 section 9.15
const max = 2048 #1
func parallelMergesortV2(s []int) {
if len(s) <= 1 {
return
}
if len(s) <= max {
sequentialMergesort(s) #2
} else { #3
middle := len(s) / 2
var wg sync.WaitGroup
wg.Add(2)
go func() {
defer wg.Done()
parallelMergesortV2(s[:middle])
}()
go func() {
defer wg.Done()
parallelMergesortV2(s[middle:])
}()
wg.Wait()
merge(s, middle)
}
}
The threshold in this implementation keeps small merge-sort sublists sequential instead of starting goroutines at every split. section 8.2.2
More goroutines do not automatically mean a faster program. On a four-core benchmark with 10,000 elements, merge sort that starts goroutines recursively down to tiny sublists runs much slower than the sequential version. A version that sorts sublists of at most 2,048 elements sequentially performs better in that benchmark. Use parallel work where its size justifies starting and coordinating goroutines. section 8.2.2






