项目里经常出现panic: assignment to entry in nil map
的错误,问题其实很简单,但第一眼不一定能看出来。
复现demo:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| package main
import ( "math/rand" "runtime" "sync" "time" )
type Task struct { Result string }
func (t *Task) f(wg *sync.WaitGroup) map[string]interface{} { defer wg.Done() time.Sleep(10*time.Microsecond) return map[string]interface{}{t.Result: rand.Int()} }
func run() { var wg sync.WaitGroup tasks := []Task{ {Result: "task1"}, {Result: "task2"}, } result := make([]map[string]interface{}, 2)
for idx, task := range tasks { wg.Add(1) go func(i int, t Task) { result[i] = t.f(&wg) }(idx, task) } wg.Wait()
result[0]["test"] = 0 result[1]["test"] = 1 }
func main() { runtime.GOMAXPROCS(2) for i := 0; i < 10000; i++ { run() } }
|
代码逻辑是期望并发执行两个任务,然后使用得到的结果,但却发生了panic。
原因是在错误的地方使用了defer wg.Done()
,内部函数f执行结束后,还没有将返回值赋值给result[i]
,主线程就继续运行了,进而发生nil map panic。
前人挖的坑,后人的kpi。