Go语言中并发写入切片不同元素是否安全?
关于你的Go并发任务结果写入切片的安全性问题
你这段代码的操作完全是线程安全的,可以放心使用,原因如下:
无数据竞争的切片访问:你在启动goroutine时,把循环的
i和job都作为参数传递给了闭包,这意味着每个goroutine拿到的都是当前循环迭代的独立副本。每个goroutine只会写入results[i]这个唯一的索引位置,不存在多个goroutine同时修改同一个内存地址的情况,自然不会产生数据竞争。这里要给你点个赞——很多人容易犯的错误是直接在闭包里引用循环变量
i,那样所有goroutine会共享同一个i变量,导致写入错误的索引位置,但你完美避开了这个坑!WaitGroup的正确使用:你在每次启动goroutine前调用
wg.Add(1),goroutine内部用defer wg.Done()确保无论任务成功还是失败(只要goroutine正常退出)都会减少计数,最后wg.Wait()会阻塞直到所有goroutine完成,保证你在后续使用results切片时,所有任务的结果都已经被正确写入。
额外的注意事项
虽然你的代码没问题,但还是提两个点帮你巩固:
- 确保
doWork函数本身是线程安全的——如果doWork内部有共享变量或者其他并发操作,那你需要额外处理,但从代码看你只是传入job,只要job是值传递(或者你确保job在goroutine里不会被其他地方修改),就没问题。 - 你的
results切片是提前分配好长度的,这非常合适。如果是用append来添加结果,那必须用互斥锁(比如sync.Mutex)保护,因为append可能会修改切片的底层数组和长度,多个goroutine同时append会导致数据竞争,但你这里直接按索引写入,完全不需要额外的同步机制。
举个反例,要是你没把i作为参数传入goroutine,错误代码会是这样:
// 错误示例!不要这么写! for i, job := range jobs { wg.Add(1) go func() { defer wg.Done() results[i] = doWork(job) // 这里的i是循环的共享变量,会导致索引混乱 }() }
但你当前的写法完全规避了这个问题,所以可以放心使用。
内容的提问来源于stack exchange,提问作者captncraig
相关产品推荐
相关产品推荐

