GO语言基础教程(157)Go并发编程之加锁机制:Go并发加锁:别让你的地鼠乱抢食!

Go并发加锁机制详解

一只萌萌的囊地鼠(Gopher)在疯狂抢食,结果撞一起了——这就是并发没加锁的写照。

1. 为什么需要锁?地鼠们的粮食争夺战

想象这样一个场景:多个goroutine同时操作同一个共享变量,就像一群地鼠争夺同一个粮仓里的食物。

package main

import (
    "fmt"
    "sync"
)

var (
    count int
    wg    sync.WaitGroup
)

func add() {
    defer wg.Done()
    for i := 1; i <= 1000000; i++ {
        count++
    }
}

func sub() {
    defer wg.Done()
    for i := 1; i <= 1000000; i++ {
        count--
    }
}

func main() {
    wg.Add(2)
    go add()
    go sub()
    wg.Wait()
    fmt.Printf("count = %d\n", count)
}

理论上,这段代码最终应该输出0,因为增加了100万次又减少了100万次。但实际运行结果却出乎意料:每次运行的结果都不一样,而且基本都不是0

为什么呢?因为count++count—这类操作并非原子操作,在底层实际上包含了多个步骤:读取值、计算、写入值。当两个goroutine交替执行时,可能会出现这样的情况:

  1. goroutine A读取count值为100
  2. goroutine B也读取count值为100
  3. goroutine A将101写入count
  4. goroutine B将99写入count

最终结果就成了99,而不是理论上应该的101。这就是数据竞争问题。

2. 互斥锁:粮仓的门牌号

为了解决上述问题,Go提供了互斥锁(Mutex),确保同一时间只有一个goroutine能访问共享资源。

import (
    "fmt"
    "sync"
)

var (
    count int
    wg    sync.WaitGroup
    lock  sync.Mutex
)

func add() {
    defer wg.Done()
    for i := 1; i <= 1000000; i++ {
        lock.Lock()   // 加锁
        count++
        lock.Unlock() // 解锁
    }
}

func sub() {
    defer wg.Done()
    for i := 1; i <= 1000000; i++ {
        lock.Lock()   // 加锁
        count--
        lock.Unlock() // 解锁
    }
}

func main() {
    wg.Add(2)
    go add()
    go sub()
    wg.Wait()
    fmt.Printf("count = %d\n", count)
}

现在这段代码每次运行都能稳定输出0了!互斥锁就像粮仓的门牌,一次只允许一只地鼠进入,其他地鼠必须排队等待。

2.1 互斥锁的正确使用姿势

使用互斥锁时,有几个关键要点:

// 推荐用法:使用defer保证锁一定会被释放
func updateShared() {
    lock.Lock()
    defer lock.Unlock() // 无论函数如何退出,都会执行解锁
    
    // 执行共享资源操作
    count++
}

// 不推荐用法:手动解锁,可能在异常情况下无法解锁
func updateSharedRisky() {
    lock.Lock()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

值引力

持续创作,多谢支持!

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

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

打赏作者

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

抵扣说明:

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

余额充值