Go 互斥锁和读写互斥锁的实现

 更新时间:2021年11月01日 09:19:56   作者:李斌的BLOG  
本文主要介绍了Go 互斥锁和读写互斥锁的实现,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

先来看这样一段代码,所存在的问题:

var wg sync.WaitGroup
var x int64

func main() {
 wg.Add(2)
 go f()
 go f()
 wg.Wait()
 fmt.Println(x) // 输出:12135
}

func f()  {
 for i:=0;i<10000;i++ {
  x = x+1
 }
 wg.Done() 
}

这里为什么输出是 12135(不同的机器结果不一样),而不是20000。

因为 x 的赋值,总共分为三个步骤:取出x的值、计算x的结果、给x赋值。那么由于对于f函数的调用采用了goroutine协程,所以存在资源竞争的问题,所以有赋值和计算过程中存在脏数据。对于此类的问题,可以采用互斥锁解决:

互斥锁

互斥锁是一种常用的控制共享资源访问的方法,它能够保证同时只有一个goroutine可以访问共享资源。Go语言中使用sync包的Mutex类型来实现互斥锁。

var wg sync.WaitGroup
var x int64
var lock sync.Mutex

func main() {
 wg.Add(2)
 go f()
 go f()
 wg.Wait()
 fmt.Println(x) // 输出:20000
}

func f()  {
 for i:=0;i<10000;i++ {
  lock.Lock() // 加互斥锁
  x = x+1
  lock.Unlock() // 解锁
 }
 wg.Done() 
}

使用互斥锁能够保证同一时间有且只有一个goroutine进入临界区,其他的goroutine则在等待锁;当互斥锁释放后,等待的goroutine才可以获取锁进入临界区,多个goroutine同时等待一个锁时,唤醒的策略是随机的。

读写互斥锁

互斥锁是完全互斥的,但是有很多实际的场景下是读多写少的,当我们并发的去读取一个资源不涉及资源修改的时候是没有必要加锁的,这种场景下使用读写锁是更好的一种选择。读写锁在Go语言中使用sync包中的RWMutex类型。

读写锁分为两种:读锁和写锁。当一个goroutine获取读锁之后,其他的goroutine如果是获取读锁会继续获得锁,如果是获取写锁就会等待;当一个goroutine获取写锁之后,其他的goroutine无论是获取读锁还是写锁都会等待。

var (
 x1 int64
 wg1 sync.WaitGroup
 lock1 sync.Mutex
 rwlock sync.RWMutex
)

func main() {
 startTime := time.Now()
 for i:=0;i<100;i++ {
  wg1.Add(1)
  write()
 }
 for i:=0;i<10000;i++ {
  wg1.Add(1)
  read()
 }
 wg1.Wait()
 fmt.Println(time.Now().Sub(startTime))

 // 互斥锁用时: 973.9304ms
 // 读写互斥锁用时: 718.094ms
}

func read()  {
 defer wg1.Done()
 //lock1.Lock() // 互斥锁
 rwlock.RLock() // 读写互斥锁
 fmt.Println(x1)
 //lock1.Unlock() // 互斥锁
 rwlock.RUnlock() // 读写互斥锁
}

func write()  {
 defer wg1.Done()
 lock1.Lock()
 x1 = x1+1
 lock1.Unlock()
}

到此这篇关于Go 互斥锁和读写互斥锁的实现的文章就介绍到这了,更多相关Go 互斥锁和读写互斥锁内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Golang语言如何高效拼接字符串详解

    Golang语言如何高效拼接字符串详解

    最近在做性能优化,有个函数里面的耗时特别长,看里面的操作大多是一些字符串拼接的操作,而字符串拼接在 golang 里面其实有很多种实现,下面这篇文章主要给大家介绍了关于Golang语言如何高效拼接字符串的相关资料,需要的朋友可以参考下
    2021-11-11
  • 特殊字符的json序列化总结大全

    特殊字符的json序列化总结大全

    这篇文章主要给大家介绍了关于特殊字符的json序列化的相关资料,通过示例代码分别给大家介绍了关于python 、 rust 、 java 和golang对特殊字符的json序列化操作,需要的朋友可以参考借鉴,下面来一起学习学习吧
    2018-09-09
  • 4个场景教会你Go中Goroutine和通道是怎么用的

    4个场景教会你Go中Goroutine和通道是怎么用的

    本篇给出了4个在运维开发工作中较为常见的且也是比较典型的场景,通过这些场景来带大家掌握Go中Goroutine和通道是怎么使用的,需要的可以学习一下
    2023-05-05
  • go-micro开发RPC服务以及运行原理介绍

    go-micro开发RPC服务以及运行原理介绍

    这篇文章介绍了go-micro开发RPC服务的方法及其运行原理,文中通过示例代码介绍的非常详细。对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-07-07
  • Golang实现Redis网络协议实例探究

    Golang实现Redis网络协议实例探究

    这篇文章主要为大家介绍了Golang实现Redis网络协议实例探究,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2024-01-01
  • Go语言变量与基础数据类型详情

    Go语言变量与基础数据类型详情

    Go 是静态(编译型)语言,是区别于解释型语言的弱类型语言(静态:类型固定,强类型:不同类型不允许直接运算),下面文章将对其进行详细介绍,需要的朋友可以参考一下
    2021-09-09
  • Go 1.22中的for循环新特性详解

    Go 1.22中的for循环新特性详解

    在 Go 语言中,for 循环是实现迭代的主要方式,Go 中的 for 循环非常灵活,有多种使用方式,本文将给大家详细的介绍一下Go 1.22中的for循环新特性,感兴趣的朋友可以参考下
    2024-02-02
  • Golang使用gin模板渲染base64图片出现#ZgotmplZ的解决办法

    Golang使用gin模板渲染base64图片出现#ZgotmplZ的解决办法

    这篇文章主要介绍了Golang使用gin模板渲染base64图片出现#ZgotmplZ的的场景复现和解决办法,文中通过代码示例讲解的非常详细,对大家解决问题有一定的帮助,需要的朋友可以参考下
    2024-05-05
  • Go语言nil标识符(空值/零值)

    Go语言nil标识符(空值/零值)

    本文主要介绍了Go语言nil标识符(空值/零值),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-03-03
  • Go Redis客户端使用的两种对比

    Go Redis客户端使用的两种对比

    这篇文章主要为大家介绍了Go Redis客户端使用对比详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-07-07

最新评论