如何在 Golang 中使用互斥锁 (Mutex)?
为了理解 Go 中的 **互斥锁 (mutex)** 在编写更出色和准确的并发程序中发挥的重要作用,我们首先必须了解称为 **竞争条件 (Race Conditions)** 的概念。让我们首先了解什么是竞争条件,以及如何编写包含竞争条件的并发程序,以及如何随后在该程序中引入 **互斥锁 (mutex)** 以使其准确。
竞争条件
**竞争条件** 是一种状态,其中多个 **goroutine** 尝试访问和修改同一资源。可能的情况是,一个 **goroutine** 试图增加某个特定变量的值,而另一个 **goroutine** 同时试图访问它,或者可能多个 **goroutine** 同时试图增加某个特定变量的值。
需要注意的是,只有当我们为特定变量提供了 **写入** 权限时,才会出现竞争条件。如果只有 **读取** 权限可用,那么就不会有任何问题,因为即使多个 **goroutine** 试图读取单个值,读取也不会导致任何问题。
示例 1
现在假设我们想为一家本地银行编写一个应用程序,该银行只支持将金额存入银行的单一功能。可能有多个人同时尝试存入金额,我们可以借助多个 **goroutine** 来表示这种情况。
请考虑下面显示的代码,该代码描述了这种情况。
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**。这三个 **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
数据结构
网络
关系型数据库管理系统 (RDBMS)
操作系统
Java
iOS
HTML
CSS
Android
Python
C 编程
C++
C#
MongoDB
MySQL
Javascript
PHP