Concurrency is dealing with many things at once; parallelism is doing many things at once. Go’s goroutines are cheap coroutines multiplexed onto OS threads by the runtime.
go func() {
// runs concurrently
}()
Protect shared mutable state.
var mu sync.Mutex
var balance int
func deposit(amount int) {
mu.Lock()
defer mu.Unlock()
balance += amount
}
Wait for a group of goroutines.
var wg sync.WaitGroup
for i := 0; i < 3; i++ {
wg.Add(1)
go func(id int) {
defer wg.Done()
work(id)
}(i)
}
wg.Wait()
Initialize exactly once.
var once sync.Once
once.Do(setup)
runtime.GOMAXPROCS controls OS threads running Go code in parallel; default is usually sensible on modern Go.
The race detector (go test -race) finds unsynchronized accesses. Prefer channels or mutexes; do not rely on “it usually works”.
Memory visibility follows happens-before rules: channel sends/receives, sync primitives, and go statement establish ordering. Read the spec when optimizing lock-free code.