Golang基础-sync.Mutex

在 Go 中,sync.Mutex 是用来实现互斥锁的,它的状态有几个不同的形式,根据锁的获取和释放状态的不同,Mutex 可以处于不同的状态。理解这些状态有助于正确地使用 Mutex 来进行同步。下面是 Mutex 可能的几种状态。

Mutex 状态概述

  1. 未锁定状态(Unlocked)

    • 这是 Mutex 的初始状态,即互斥锁未被任何 goroutine 持有。

    • 在这种状态下,任何 goroutine 都可以成功地获取锁。

  2. 锁定状态(Locked)

    • 这个状态表示当前有一个 goroutine 已经成功地获取了锁,其他 goroutine 需要等待锁的释放。

    • 在一个 goroutine 获取了 Mutex 之后,它会持有锁,直到调用 Unlock() 释放锁。

    • 锁定的状态下,其他 goroutine 再尝试获取这个锁会被阻塞,直到锁被释放。

  3. 等待状态(Waiting)

    • 当多个 goroutine 同时尝试获取锁时,除了一个成功获取锁的 goroutine 之外,其他的 goroutine 都会进入等待状态。

    • 这些等待的 goroutine 会阻塞在 mutex.Lock() 调用处,直到获得锁并且它们不再被阻塞。

  4. 死锁状态(Deadlock)

    • 死锁 并不是 Mutex 的一个明确的状态,但它是一个严重的并发错误。死锁发生时,多个 goroutine 相互等待对方释放锁,导致程序无法继续执行。

    • 死锁通常发生在 goroutine 在持有锁时再尝试获取已经被其他 goroutine 持有的锁,从而导致无限期的阻塞。

Mutex 内部实现的简化描述

Go 的 sync.Mutex 底层实现是基于 互斥锁,并且通过标志位和等待队列来控制访问。它的状态管理方式如下:

  1. 空闲状态:锁没有被任何 goroutine 持有,任何 goroutine 都可以调用 Lock() 来获取锁。

  2. 锁定状态:当某个 goroutine 调用 Lock() 成功时,Mutex 会被标记为 "locked"(锁定状态),这个锁会保持直到该 goroutine 调用 Unlock() 来释放锁。

  3. 等待状态:当锁已被某个 goroutine 获取,其他 goroutine 再尝试获取时,这些 goroutine 会进入阻塞状态并挂起。Go 会通过操作系统的线程调度机制来管理这些 goroutine,直到锁被释放。

  4. 释放锁后:当一个持有锁的 goroutine 执行完任务并调用 Unlock() 时,Mutex 会从锁定状态变回未锁定状态,且可能会唤醒等待的 goroutine,允许其中一个 goroutine 获取锁并进入锁定状态。

示例:Mutex 使用和状态变更

package main
​
import (
    "fmt"
    "sync"
)
​
var mu sync.Mutex // 创建一个 Mutex 对象
​
func criticalSection(id int) {
    // 获取锁
    mu.Lock()  // 锁定状态,阻塞其他 goroutine
    defer mu.Unlock()  // 在函数结束时解锁,返回未锁定状态
​
    // 模拟临界区操作
    fmt.Printf("Goroutine %d is in critical section\n", id)
}
​
func main() {
    var wg sync.WaitGroup
    for i := 1; i <= 3; i++ {
        wg.Add(1)
        go func(id int) {
            defer wg.Done()
            criticalSection(id)
        }(i)
    }
​
    wg.Wait()
}

状态变更流程:

  1. 初始状态(Unlocked):程序启动时,mu 是未锁定的状态,任何 goroutine 都可以成功获取锁。

  2. 锁定状态(Locked):第一个 goroutine 调用 mu.Lock() 后成功获取锁,其他 goroutine 会阻塞,等待锁的释放。

  3. 释放锁后:第一个 goroutine 执行完临界区代码后,调用 mu.Unlock(),锁被释放,其他被阻塞的 goroutine 中的一个会被唤醒并成功获取锁,进入锁定状态。

死锁示例:

死锁发生的一个典型情况是互相等待对方释放锁。以下是一个简单的死锁示例:

package main
​
import "sync"
​
var mu1 sync.Mutex
var mu2 sync.Mutex
​
func func1() {
    mu1.Lock()
    defer mu1.Unlock()
    mu2.Lock()
    defer mu2.Unlock()
}
​
func func2() {
    mu2.Lock()
    defer mu2.Unlock()
    mu1.Lock()
    defer mu1.Unlock()
}
​
func main() {
    go func1()
    go func2()
}

死锁分析

  • func1() 获取了 mu1,然后试图获取 mu2

  • func2() 获取了 mu2,然后试图获取 mu1

  • 由于两个 goroutine 互相等待对方释放锁,导致了死锁。

总结

  • 未锁定状态(Unlocked)Mutex 没有被任何 goroutine 持有。

  • 锁定状态(Locked):一个 goroutine 获取了锁,其他 goroutine 需要等待。

  • 等待状态(Waiting):多个 goroutine 同时请求获取同一个锁,未获得锁的 goroutine 进入等待。

  • 死锁状态(Deadlock):多个 goroutine 因为相互等待对方释放锁而导致程序无法继续执行。

Go 提供的 Mutex 是一个非常简单但强大的并发控制工具,通过理解它的不同状态,可以更好地管理并发代码中的同步。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Yy_Yyyyy_zz

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值