如何在 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

更新于: 2021年11月1日

317 次浏览

开启你的 职业生涯

通过完成课程获得认证

开始学习
广告

© . All rights reserved.