Тут суть в том, что в момент, когда анонимная функция начнёт выполняться - выполнение цикла к тому моменту уже закончится и i будет равна 10.
Т.е. шедулер не успевает запустить горутины, они фактически запускаются уже после цикла, но часть из них может успеть и во время цикла запуститься...
Самый простой способ получить желаемый результат передать значение как параметр
go func(idx int) {
...
}(i)
Полный пример:
package main
import (
"sync"
)
const N = 10
func main() {
m := make(map[int]int)
wg := &sync.WaitGroup{}
mu := &sync.Mutex{}
wg.Add(N)
for i := 0; i < N; i++ {
k := 5
go func(idx int) {
defer wg.Done()
mu.Lock()
println(idx, k)
m[idx] = k
mu.Unlock()
}(i)
}
wg.Wait()
println(len(m))
println(m[0])
}