【GoLang】如何在高并发情况下保证map的安全

Go语言中的map是一种关联容器,它存储键值对,并且键是唯一的。在高并发情况下,如果多个goroutine同时对同一个map进行读写操作,可能会引发数据竞争,导致不可预知的行为,比如数据丢失、不一致性,甚至程序崩溃。

发生场景

如果有两个或更多的 goroutine 尝试同时修改同一个 map,比如添加、删除或更新键值对,就会发生并发安全问题,报错 fatal error: concurrent map writes
下图例子表示2个goroutine同时对一个map的同一个key的值进行自增10000次,最后打印出这个key的值。

运行结果有两种:

一种是报错

一种是输出一个不为20000的数

那么我们如何避免上图的情况,保证map的并发安全,最后输出正常的结果20000呢?以下由3种方案供选择。

解决方式

1. 使用互斥锁(Mutex)

互斥锁是一种基本的同步原语,可以确保在同一时刻只有一个goroutine可以访问map,有效地避免并发读写导致的数据竞争问题。在Go语言中,我们使用sync包中的Mutex结构体来实现互斥锁。

使用方法:

首先,声明互斥锁的全局变量。

接着,在map的读写操作前锁定互斥锁,在map的读写操作后解锁互斥锁。

最后,运行查看输出结果是否正常。

输出结果正常

2. 使用sync.map

sync.Map 是 Go 语言在 1.9 版本引入的一种并发安全的 map 类型,它提供了一种在多个 goroutine 中并发读写 map 的方式,而不需要程序员手动管理锁。sync.Map 内部通过特殊的设计来优化并发性能,它通常比使用互斥锁保护的普通 map 在高并发场景下有更好的性能表现。

package main

import (
	"fmt"
	"sync"
	"time"
)

var counter sync.Map

func incrementCounter() {
	for i := 0; i < 10000; i++ {
		value, _ := counter.Load("count")
		counter.Store("count", value.(int)+1)
	}
}

func main() {
	counter.Store("count", 0)

	// 启动两个goroutine来增加counter
	go incrementCounter()
	go incrementCounter()

	// 等待一段时间,让goroutine有机会运行
	time.Sleep(time.Second)

	// 打印counter的值
	value, _ := counter.Load("count")
	fmt.Println("Final Counter:", value)
}

需要注意的是,此方法只能保证程序不报错,但最后输出的结果不一定是20000 。

这是因为对于并发的计数操作,使用sync.Map仍然无法保证原子性,因为每次加载和存储操作都是独立的,无法保证在同一时刻只有一个goroutine在执行这些操作。

  • 5
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值