美文网首页
golang中的互斥锁与读写锁

golang中的互斥锁与读写锁

作者: 护念 | 来源:发表于2023-11-11 20:08 被阅读0次

在多线程执行情况下,会存在对同一变量同时修改的情况;不能保证资源的修改后数据的一致(和我们期望)我们称为线程不安全。

这种情况,在编程语言中我们可以通过加锁解决。

我们先来看在不加锁的情况下,会产生的问题。

package main

import (
    "fmt"
    "sync"
)

var w sync.WaitGroup

type Account struct {
    name   string
    amount int
}

// 取钱
func (a *Account) WithDraw(num int) {
    a.amount -= num
}

// 存钱
func (a *Account) Deposit(num int) {
    a.amount += num
}

func main() {
    a := Account{
        name:   "zhangsan",
        amount: 0, // 初始化金额为0
    }

    w.Add(2)
    // 开一个gorounine 存钱
    go func() {
        defer w.Done()
        for i := 0; i < 1000; i++ {
            a.Deposit(100) // 每次存100
        }
    }()

    // 开一个gorountine 取钱
    go func() {
        defer w.Done()
        for i := 0; i < 1000; i++ {
            a.WithDraw(50) // 每次取50
        }
    }()

    w.Wait()              // 等待所有gorountine执行完
    fmt.Println(a.amount) // 最终金额
}

多次执行,我们会发现每次执行后打印的值都不一样,因为,并发情况下对amount的修改是非线程安全的。

下面我们开始修复它。

1. sync.Mutext互斥锁

在修改amount金额时,先加锁;修改完释放锁;
代码如下:

package main

import (
    "fmt"
    "sync"
)

var (
    lock sync.Mutex
    w    sync.WaitGroup
)

type Account struct {
    name   string
    amount int
}

// 取钱
func (a *Account) WithDraw(num int) {
    lock.Lock() // 加锁
    a.amount -= num
    lock.Unlock() //释放锁
}

// 存钱
func (a *Account) Deposit(num int) {
    lock.Lock() // 加锁
    a.amount += num
    lock.Unlock() //释放锁
}

func main() {
    a := Account{
        name:   "zhangsan",
        amount: 0, // 初始化金额为0
    }

    w.Add(2)
    // 开一个gorounine 存钱
    go func() {
        defer w.Done()
        for i := 0; i < 1000; i++ {
            a.Deposit(100) // 每次存100
        }
    }()

    // 开一个gorountine 取钱
    go func() {
        defer w.Done()
        for i := 0; i < 1000; i++ {
            a.WithDraw(50) // 每次取50
        }
    }()

    w.Wait()              // 等待所有gorountine执行完
    fmt.Println(a.amount) // 最终金额
}

现在执行代码每次输出都是50000,数据正确啦~

2. sync.RWMutex 读写锁

sync.Mutex可以解决问题,但是在许多场景下,都是读多写少;在这种情况下,使用读写锁更好。

什么事读写锁呢?

  1. 在一个线程的对读加锁的情况,其它线程也可以读
  2. 只有在线程写的情况下,其它线程不能读和写

总结下就是,只有写的时候会造成阻塞,其它时候不会阻塞;因此非常时候读多写入。

其写法和互斥差不多,只是在读的时候,写法不同多了R

  • lock.RLock() 加读锁
  • lock.RUnlock() 释放读锁

我们来看看代码:

package main

import (
    "fmt"
    "sync"
    "time"
)

var (
    x    = 0
    lock sync.RWMutex // 读写锁
    w    sync.WaitGroup
)

// 读
func read() {
    lock.RLock()                     // 读上锁
    time.Sleep(5 * time.Millisecond) // 模拟耗时操作
    lock.RUnlock()                   //读释放锁
}

// 写
func write() {
    lock.Lock() // 写加锁 (和互斥锁写法一样)
    x += 1
    time.Sleep(50 * time.Millisecond) // 模拟耗时间操作
    lock.Unlock()
}

func main() {
    w.Add(2)
    // 开一个gorounine 读(多)
    go func() {
        defer w.Done()
        for i := 0; i < 1000; i++ {
            read()
        }
    }()

    // 开一个gorountine 写
    go func() {
        defer w.Done()
        for i := 0; i < 10; i++ { // 写的少
            write()
        }
    }()

    w.Wait()       // 等待所有gorountine执行完
    fmt.Println(x) // 打印x值
}

相关文章

  • Golang 锁的相关知识

    Golang锁分类:互斥锁(Mutex)、读写锁(RWMutex)。 互斥锁 在编写代码中引入了对象互斥锁的概念,...

  • go-锁机制

    Golang中的锁机制主要包含互斥锁和读写锁 互斥锁 互斥锁是一种简单的加锁的方法来控制对共享资源的访问,互斥锁只...

  • golang 中 sync.Mutex 和 sync.RWMut

    介绍 golang 中的 sync 包实现了两种锁: Mutex:互斥锁 RWMutex:读写锁,RWMutex ...

  • golang笔记之基于共享变量的并发

    介绍 golang 中的 sync 包实现了两种锁: Mutex:互斥锁 RWMutex:读写锁,RWMutex ...

  • Golang 互斥锁与读写锁

    互斥锁 代码示例 打印结果 Lock the lock. (G0)The lock is locked.(G0)-...

  • 技术好文集合

    缓存热点key 大value问题,解决方案 golang sync.Map 如何理解互斥锁、条件锁、读写锁以及自旋锁?

  • 可重入读写锁 ReentrantReadWriteLock

    读写锁分为读锁和写锁,多个线程获取读锁不互斥,读写锁、写写锁互斥。 输出

  • 读写锁和互斥锁 读写互斥锁,简称读写锁 mux sync.RWMutex Lock和Unlock分别对写锁进行锁定...

  • Golang 读写锁RWMutex 互斥锁Mutex 源码详解

    前言 Golang中有两种类型的锁,Mutex (互斥锁)和RWMutex(读写锁)对于这两种锁的使用这里就不多说...

  • Go 语言的锁

    Go 语言提供两类锁: 互斥锁(Mutex)和读写锁(RWMutex)。其中读写锁(RWMutex)是基于互斥锁(...

网友评论

      本文标题:golang中的互斥锁与读写锁

      本文链接:https://www.haomeiwen.com/subject/zeclwdtx.html