关于Go语言的竞态问题以及互斥锁问题 | 青训营笔记

114 阅读2分钟

这是我参与「第三届青训营 -后端场」笔记创作活动的第4篇笔记。

1 竞态问题

Go语言以构建高并发容易、性能优异而闻名。但是,伴随着并发的使用,可能发生可怕的数据争用的竞态问题。而一旦遇到竞态问题,由于不知道其什么时候发生,所以将产生难以发现和调试的错误。

func main() {
    fmt.Println(getNameber())
}

func getNumber() int{
    var i int
    go func() {
        i = 6
    }()
    
    return i
}

在上图的实例中,getNumber()函数先声明一个变量i,之后再goroutine中单独对i进行设置。而这是成语也正在从函数中返回i,由于不知道goroutine是否完成对i值的修改,所以酱油两个操作发生:

  1. 如果goroutine已完成对i值的修改,则最后返回的i值为6
  2. 如果goroutine未完成对i值的修改,则变量i的值从函数返回,为默认值0 这就是竞态问题。

为了避免竞态问题,Go提供了很多解决方法,比如通道阻塞、互斥锁等。 发生了竞态,就要想办法解决。总的来说,解决竞态有三种办法:

  1. 不要修改变量 如果一个变量不需要修改,在任何地方访问都是安全的,但这个方法却无法解决上面的问题。
  2. 不要多个 goroutine 中去访问同一个变量 我们前面说聊过的 goroutine + channel 就是这样的一个思路,通过 channel 阻塞来更新变量,这也符合 Go 代码的设计理念:不要通过共享内存来通信,而应该通过通信来共享内存
  3. 同一时间只允许一个 goroutine 访问变量
    如果在同一时间只能有一个 goroutine 访问变量,其他的 goruotine 需要等到当前的访问结束之后,才能访问,这样也可以消除竞态,下面将要说到的工具就是用来保证同一时间只能有一个 goroutine 来访问变量。