Go语言线程安全之互斥锁与读写锁
作者:酷尔。 发布时间:2024-05-09 09:56:18
前言:
单个线程时数据操作的只有一个线程,数据的修改也只有一个线程参与,数据相对来说是安全的,多线程时对数据操作的不止一个线程,所以同时对数据进行修改的时候难免紊乱
一、互斥锁是什么?
1.概念
互斥锁是为了并发的安全,在多个goroutine
共同工作的时候,对于共享的数据十分不安全写入时容易因为竞争造成数据不必要的丢失。互斥锁一般加在共享数据修改的地方。
2.未加锁
线程不安全,操作的全局变量会计算异常
package main
import (
?? ?"fmt"
?? ?"sync"
)
var x int = 0
var wg sync.WaitGroup
func add() {
?? ?defer wg.Done()
?? ?for i := 0; i < 5000; i++ {
?? ??? ?x++
?? ?}
}
func main() {
?? ?wg.Add(2)
?? ?go add()
?? ?go add()
?? ?wg.Wait()
?? ?fmt.Println(x)
}
/*
打印结果:(每次打印不一样,正常的结果应该是10000)
?? ?6051
?? ?5059
?? ?5748
?? ?10000
*/
3.加锁之后
线程安全,全局变量计算无异常
package main
import (
?? ?"fmt"
?? ?"sync"
)
var x int = 0
var wg sync.WaitGroup
// 创建一个锁对象
var lock sync.Mutex
func add() {
?? ?defer wg.Done()
?? ?for i := 0; i < 5000; i++ {
?? ??? ?//加锁
?? ??? ?lock.Lock()
?? ??? ?x++
?? ??? ?//解锁
?? ??? ?lock.Unlock()
?? ?}
}
func main() {
?? ?wg.Add(2)
?? ?//开启两个线程
?? ?go add()
?? ?go add()
?? ?wg.Wait()
?? ?fmt.Println(x)
}
/*
打印结果:
?? ?全为10000
*/
二、读写锁【效率革命】
1.为什么读写锁效率高
使用锁的时候,安全与效率往往需要互相转换,对数据进行操作的时候,只会进行数据的读与写。 而读与读之间可以同时进行,读与写之间需要保证写的时候不去读。此时为了提高效率就发明读写锁,在读写锁机制下,安全没有丝毫降低,但效率进行了成倍的提升提升的效率在读与写操作次数差异越大时越明显
2.使用方法
代码如下(示例):
package main
import (
?? ?"fmt"
?? ?"sync"
?? ?"time"
)
var (
?? ?x ? ? ?= 0
?? ?rwlock sync.RWMutex
?? ?wg ? ? sync.WaitGroup
)
func write() {
?? ?defer wg.Done()
?? ?rwlock.Lock()
?? ?x++
?? ?rwlock.Unlock()
}
func read() {
?? ?wg.Done()
?? ?//开启读锁
?? ?rwlock.RLock()
?? ?fmt.Println(x)
?? ?//释放读锁
?? ?rwlock.RUnlock()
}
func main() {
?? ?start := time.Now()
?? ?for i := 0; i < 100; i++ {
?? ??? ?wg.Add(1)
?? ??? ?go write()
?? ?}
?? ?// time.Sleep(time.Second)
?? ?for i := 0; i < 10000; i++ {
?? ??? ?wg.Add(1)
?? ??? ?go read()
?? ?}
?? ?wg.Wait()
?? ?fmt.Println(time.Now().Sub(start))
}
三、sync.once
1.sync.once产生背景
在多个goroutine
中往往会由于线程不同步造成数据读写的冲突,特别是在进行文件打开对象创建的时候,可能会造成向关闭的文件写内容,使用未初始化的对象,或者对一个对象进行多次初始化。
2.sync.once机制概述
sync.once
保证函数内的代码只执行一次, 实现的机制是在once内部有一个标志位,在执行代码的时候执行一次之后标志位将置为1后续判断标志位,如果标志位被改为1则无法再进行操纵
3.sync.once注意点
sync.Once.Do()
传进去的函数参数无参无返,一个once对象只能执行一次Do方法,向Do方法内传多个不同的函数时只能执行第一个传进去的,传进去Do方法的函数无参无返,可以用函数闭包把需要的变量传进去
4.使用方法
一般结合并发使用,旨在对通道或文件只进行一次关闭
func f2(a <-chan int, b chan<- int) {
?? ?for {
?? ??? ?x, ok := <-a
?? ??? ?if !ok {
?? ??? ??? ?break
?? ??? ?}
?? ??? ?fmt.Println(x)
?? ??? ?b <- x * 10
?? ?}
?? ?// 确保b通道只关闭一次
?? ?once.Do(func() {
?? ??? ?close(b)
?? ?})
}
四、atomic原子包操作
原子包将指定的数据进行安全的加减交换操作; 网上还有一大堆关于原子包的api感兴趣的小伙伴可以自行百度,这里就不细细阐述了
package main
import (
?? ?"fmt"
?? ?"sync"
?? ?"sync/atomic"
)
var x int64 = 0
var wg sync.WaitGroup
/*
?? ?原子操作是将数据进行打包枷锁,直接通过指定的函数进行相应的操作
?? ?可以使用load读取、store写入、add修改、swap交换。
?? ?// 类似于读取一个变量、对一个变量进行赋值
*/
func addone() {
?? ?// 没有加锁进行并发的话,会产生数据丢失的情况
?? ?defer wg.Done()
?? ?// x++
?? ?// 不用加锁也可以使用的行云流水
?? ?// 第一个参数是进行操作的数据,第二个是增加的步长
?? ?atomic.AddInt64(&x, 1)
}
func csf() {
?? ?// 进行比较相等则将新值替换旧值
?? ?ok := atomic.CompareAndSwapInt64(&x, 100, 200)
?? ?fmt.Println(ok, x)
}
func main() {
?? ?for i := 0; i < 50000; i++ {
?? ??? ?wg.Add(1)
?? ??? ?go addone()
?? ?}
?? ?wg.Wait()
?? ?fmt.Println(x)
?? ?x = 100
?? ?csf()
?? ?fmt.Println(123)
}
总结:
读写锁区分读者和写者,而互斥锁不区分 互斥锁同一时间只允许一个线程访问该对象,无论读写;读写锁同一时间内只允许一个写者, 但是允许多个读者同时读对象。 联系:读写锁在获取写锁的时候机制类似于互斥锁。
来源:https://blog.csdn.net/apple_51931783/article/details/122553203
猜你喜欢
- 这几天正在为压缩代码的事情所困扰,大家也可以看见,我的博客顶端有两个在线的压缩工具,但在实际应用过程中,除了CSS的压缩比较满意外,JS的压
- 我们都有过函数调用的经历,那么call调用类实例的过程就跟函数很相似。类的用法很多人都知道了,类实例又是什么呢?可以把类看成一个设计图,类实
- 在JavaScript中对字符串进行转义和反转义操作,常用的方法莫过于使用encodeURI (decodeURI)、encodeURICo
- 选择排序:选择排序(Selection sort)是一种简单直观的 排序算法 。它的工作原理如下。首先在未排序序列中找到最小(大)元素,存放
- 本文实例讲述了python通过pil模块获得图片exif信息的方法。分享给大家供大家参考。具体分析如下:python的pil模块功能超级强大
- 本文实例讲述了Python类装饰器。分享给大家供大家参考,具体如下:编写类装饰器类装饰器类似于函数装饰器的概念,但它应用于类,它们可以用于管
- 一、项目概述本次项目目标是实现对自动生成的带有各种噪声的车牌识别。在噪声干扰情况下,车牌字符分割较困难,此次车牌识别是将车牌7个字符同时训练
- Hello 大家好,我是TANZAME,我们又见面了。NuGet是什么这里就不再重复啰嗦,园子里一搜一大把。今天要跟大家分享的是,在日常开发
- object本身就是无对象的集合,因此在用 for-in 语句遍历对象的属性时,遍历出的属性顺序与对象定义时不同。了解W3C标准:根据 EC
- 如今,随着网站的越来越普及,与Web相关的开发技术持续热门,从前端到后端,从标记语言到开发语言,各种技术交相辉映,沉沉浮浮,从开始简单的ht
- 首先要介绍的是 Python Imaging Library,使用方法如下:from PIL import Imagefrom PIL.Ex
- 安装Python2.7后,它自带一个编辑器IDLE,但是使用几次之后出现启动不了的情况,可做如下操作。Windows操作系统下,使用快捷键
- Anaconda 本质上是一个软件发行版,包含了 conda、Python 等 180 多个科学包及其依赖项。因为包含了大量的科学包,Ana
- 秉承着一切皆对象的理念,我们再次回头来看函数(function)。函数也是一个对象,具有属性(可以使用dir()查询)。作为对象,它还可以赋
- 目录前言总结:1.代码块的缓存机制2.小数据池3.优缺点小整数对象池大整数对象池字符串驻留机制简单原理:前言本文除"总结"
- 本文实例为大家分享了python绘制直方图的具体代码,供大家参考,具体内容如下运行结果如下代码如下from matplotlib impor
- 首先,必须有错误继续进行的声明On Error Resume Next 然后尝试简历jmail实例: Dim JMail Set JMail
- 一、测试模型下面这部分来自于某书籍资料,拿过来,按需参考一下:测试模型(1)线性测试1、概念:通过录制或编写对应应用程序的操作步骤产生的线性
- 前言在工作中使用的是oracle数据库,平时想在家测试一些sql是否可以跑的过,可惜自己电脑并没有安装oracle数据库,甚至完全不想安装到
- 一、单表查询—>更新UPDATE table_nameSET field1=new-value1, field2=new-value2