Go包级变量替换是否线程安全?附代码示例分析
package maptest var oldMap = map[string]string{"key1": "old_value1", "key2": "old_value2", "key3": "old_value3"} func updateMap() { oldMap = map[string]string{"key1": "new_value1", "key2": "new_value2", "key3": "new_value3"} } func action1() string { return oldMap["key1"] } func action2() { var resultKey string var resultValue string for k, v := range oldMap { resultKey += oldMap[k] resultValue += v } }
问题1解答
不会引发panic,action1()要么返回旧值要么返回新值,不会出现中间状态。
原因是:oldMap作为map类型本质是一个指针,updateMap()的操作是给这个指针变量赋一个新的map地址,这种单个机器字的赋值操作在Go中是原子性的——CPU会一次性完成赋值,不会出现指针只修改一半的无效状态。所以action1()读取oldMap时,要么拿到旧map的指针,要么拿到新map的指针,只会返回对应map里key1的有效值,不会触发panic。
问题2解答
执行过程中会出现resultKey和resultValue拼接内容来自不同map的情况,你的假设基本正确:
- 当
updateMap()被调用后,后续循环里的oldMap[k]会访问新map,但v依然是旧map中对应键的value副本。 - 使用
v是完全安全的:因为string是值类型,v是遍历旧map时已经复制到栈上的具体值,和旧map是否被回收无关,肯定能拿到旧map里的正确数据。 - 但
resultKey的取值会出问题:循环开始时,range是基于旧map的引用遍历键,但每次循环里的oldMap[k]是读取当前oldMap变量指向的map——如果updateMap()已经替换了oldMap的引用,这里就会取新map的k对应值,和v可能完全不匹配。比如旧map的key1对应old_value1(赋值给v),但新map的key1是new_value1,此时resultKey会拼接new_value1,而resultValue拼接old_value1。 - 整个过程不会panic,因为没有并发修改同一个map:旧map只是不再被引用,没有被修改;新map也只是被读取,没有修改操作,不会触发map并发读写的panic。
内容的提问来源于stack exchange,提问作者ragarac3
相关产品推荐
相关产品推荐

