Golang 如何使用Mutex
为了理解为什么 Mutex 在Go中对编写更好更准确的并发程序起着重要的作用,我们必须首先了解一个叫做 Race Conditions 的概念 。 让我们先了解一下什么是竞赛条件,我们如何编写一个具有竞赛条件的并发程序,以及我们如何在该程序中引入 互斥 来使其准确。
竞赛条件
竞赛条件 是指多个 goroutine 试图访问和修改同一资源的情况。它可能是一个 goroutines 试图增加一个特定变量的值,而其他 goroutines 同时试图访问它,或者它可能是多个 goroutines 同时试图增加一个特定变量的值。
应该注意的是,只有当我们为某一特定变量提供了 写入 权限时,才会出现竞赛条件。如果只提供了 读的 权限,那么就不会有任何问题,因为即使多个 goroutines 试图读取一个值,读也不会造成任何问题。
例子1
现在让我们假设我们想为一个本地银行写一个应用程序,银行只支持一个单一的功能,即把钱存入银行。我们可以用多个 goroutines 来表示这种情况,因为有可能有很多人同时试图存入金额。
请看下面的代码,它描述了这种情况。
package main
import (
"fmt"
"sync"
)
var (
balance int
wg sync.WaitGroup
)
func Deposit(amount int) {
balance = balance + amount
wg.Done()
}
func main() {
wg.Add(3)
go Deposit(100)
go Deposit(200)
go Deposit(300)
wg.Wait()
fmt.Println("Balance is:", balance)
}
在上面的例子中,我们可以看到,除了 主 函数goroutine之外,还有三个 goroutine 。这三个 goroutine 正在调用 deposit 函数,由于这个原因,出现了一个竞赛条件,因为我们还没有处理它。
在 “race “ 标志的帮助下,人们可以确认竞赛条件的存在。
go run -race main.go
注意 – 种族标志 是用来检查任何Golang代码是否有种族条件。
输出
Balance is: 600
为了使代码更加准确,并消除这种竞赛条件,我们利用 Mutex ,也称为 互斥 ,它可以防止并发进程在执行另一个任务时访问关键数据。
例2
请看下面的代码,我们在上面的代码中使用了 Mutex ,以消除竞赛条件。
package main
import (
"fmt"
"sync"
)
var (
balance int
wg sync.WaitGroup
mu sync.Mutex
)
func Deposit(amount int) {
mu.Lock()
defer mu.Unlock()
balance = balance + amount
wg.Done()
}
func main() {
wg.Add(3)
go Deposit(100)
go Deposit(200)
go Deposit(300)
wg.Wait()
fmt.Println("Balance is:", balance)
}
输出
现在,如果我们运行命令, go run -race main.go ,那么我们就不会看到任何提到的竞赛条件。我们得到的输出如下所示。
Balance is: 600