前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >高并发情况下怎么保证读写数据正常?

高并发情况下怎么保证读写数据正常?

作者头像
小锟哥哥
发布2022-05-10 08:58:36
3400
发布2022-05-10 08:58:36
举报
文章被收录于专栏:GoLang全栈

现在写程序,如果说不会处理并发问题,那很可能会被人吐槽跟不上时代的步伐了。

并且我们的 Go 语言在处理并发问题上,有着天然的优势,简直是非常的丝滑。

所以我决定,近期我会出几期关于并发编程上的分享。

什么是并发问题?

并发问题的根本还是来自我们的计算机性能的提升,由最原始的单核单线程到现在的多核多线程。

形象的理解就像是一个有钱人家,从最开始的只请了一个人帮他干活,到现在请了多个人干活一样。

人一多,自然而然在协作上就容易出现信息不同步等问题,这就是并发问题。

演变到程序里面就像下面这张图:

假设我们有一个变量,现在我们同时有4个方法甲乙丙丁去修改它,那就非常容易出现,乙在取这个值的时候,此时甲还在操作。

没有线程安全的计数器

我们在某些计数器时,比如用户调用 API 次数的统计时,在高并发的情况下就非常容易出现线程不安全的问题,请看下面的代码:

代码语言:javascript
复制
type DownInfo struct {
 DownCount int
}

func NewDownInfo() *DownInfo {
 return &DownInfo{}
}

func (this *DownInfo) AddCount() {
 this.DownCount = this.DownCount+1
}

func main() {
 down := NewDownInfo()
 
 var wg sync.WaitGroup
 wg.Add(100)
 for i := 0; i < 100; i++ {
  go func() {
   defer wg.Done()
   for i := 0; i < 100; i++ {
    down.AddCount()
   }
  }()
 }
 wg.Wait()
 fmt.Println(down)
}

我在 main 方法里面模拟了 100 个异步线程,然后每个线程里面再模拟了 100 个操作请求,合计 10000 个。

如果不加入异步并发的情况下,正常输出的结果应该是 10000 ,但是这段代码加入了异步并发,执行的结果就非常诡异了,每次执行的结果都会不一样:

啥原因呢?

我们的 AddCount 方法里面,是基于之前的结果进行 +1 然后再赋值给变量。

在高并发情况下,于是就出现了你取的值并不是最新的值,此时很可能还有别的调用者正在做 +1 操作还没来得及回写的情况下。

解决问题

上面抛出了问题所在,下面我们来解决这个问题:

刚才我们提到了并发问题是出在同一时刻被多个方法操作,所以我们只需要让他们同时只允许一个方法调用就好了,如下图:

在 Go 的官方包里面有一个库 sync 提供了很多帮助我们解决并发问题的工具,面对上面的问题,我们可以使用 Mutex 互斥锁来解决这个问题。

互斥锁 又叫 排它锁,他每次只允许一个方法操作,我们一般是配合隐式声明一起使用:

代码语言:javascript
复制
type DownInfo struct {
 DownCount int
 sync.Mutex
}

func NewDownInfo() *DownInfo {
 return &DownInfo{}
}

func (this *DownInfo) AddCount() {
 this.Lock()
 this.DownCount = this.DownCount+1
 this.Unlock()
}

我们只需要调整 AddCount 方法就可以了;

Lock 和 Unlock 之间的代码都是线程安全的!

所以我们只需要在 +1 之前上锁,操作完之后再解锁即可。

其他地方的代码不变!

再次执行,每次的结果都是 10000 了!

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-11-30,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 GoLang全栈 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 什么是并发问题?
  • 没有线程安全的计数器
    • 啥原因呢?
    • 解决问题
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档