如何在 Golang 中使用互斥锁?
为了理解为什么 Go 中的互斥锁在编写更好、更准确的并发程序方面发挥着重要作用,我们首先必须了解竞争条件的概念。让我们首先了解什么是竞争条件,如何编写一个包含竞争条件的并发程序,以及如何在该程序中引入互斥锁以使其更准确。
竞争条件
竞争条件是指多个goroutine尝试访问和修改同一资源的情况。可能的情况是,一个 goroutines 尝试增加某个变量的值,而其他 goroutines 也尝试同时访问该变量;也可能是多个 goroutines 尝试同时增加某个变量的值。
需要注意的是,只有当我们为某个变量提供了 write 权限时,才会出现竞争条件。如果只有 read 权限,那么就不会出现任何问题,因为即使多个 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) }
在上面的例子中,我们可以看到除了 main 函数之外,还有三个 goroutine。这三个 goroutine 正在调用 deposit 函数,因此存在竞争条件,因为我们还没有处理它。
可以使用 "race" 标志来确认是否存在竞争条件。
go run -race main.go
注意 − race 标志用于检查任何 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