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
猜你喜欢
- selenium IDE是干什么的Selenium IDE 是一个简单的录制回放工具,它可以录制你在浏览器上的操作,回放脚本时
- 照例使用XMLhttp同步方式获取数据,可是由于网络不稳定,经常造成'死锁'状况,既send之后一直不返回服务器结果,也不出
- 前言相信大家在日常使用mysql,可能会遇到需要同时更新两张表时,我会采用在同一个事务中使用2句sql语句分别进行更新。其实,这种需要发送2
- urls.py:URL dispatcher(路由配置文件)URL配置(URLconf)就像是Django所支撑网站的目录。它的本质是URL
- FBV:function based view 基于函数的视图.CBV:class based view 基于类的视图.在视图函数创建类,需
- 抽象工厂模式(Abstract Factory Pattern):属于创建型模式,它提供了一种创建对象的最佳方式。在抽象工厂模式中,接口是负
- drop table if exists dd; create table dd ( user_id int , class_no int
- 熵值法也称熵权法,是学术研究,及实际应用中的一种常用且有效的编制指标的方法。1.简单理解 信息熵机器学习中的决策树算法是对信息熵的一种典型的
- 有的时候,一个 if … else … 还不够用。比如,根据年龄的划分:条件1:18岁或以上:adult条件2:6岁或以上:teenager
- 查看表空间的名称及大小代码如下:SQL>select t.tablespace_name, round(sum(bytes/(1024
- 最近论坛里总有人问幻灯片怎么从数据库里取数据,花了几分钟简单的写了下。用到的人可以自己在细化<%dim rs,sqlset&
- 统计数据行数 SELECT COUNT() FROM 语法用于从数据表中统计数据行数。 语法: SELECT COUNT(column) F
- 一.主键:1.1)主键字段定义:在数据库表中,如果有一组字段能够唯一确定一条记录,则可以把它们设计成表的主键字段。例子:如果要创
- 需求是:针对三篇英文文章进行分析,计算出现次数最多的 10 个单词逻辑很清晰简单,不算难, 使用 python 读取多个 txt 文件,将文
- 一、django的模板:在settings.py的文件中可以看到并设置这个模板。1.直接映射:通过建立的文件夹(templates)和文件(
- 必备环境废话每年回家都要帮我爸下些音乐,这对我来说都是轻车熟路!可当我打开网易云点击下载按钮的时候,可惜已物是人非啦!开个 VIP 其实也不
- 前言许久之前用 Mirai 搭建了 QQ 机器人,不过因为云服务器到期了,QQ 机器人被 迫下线,现如今,可能是意犹未尽,今天就基于 go-
- 作用:pygame一般用来做游戏注意:1.在使用pygame提供的功能之前,需要调用init方法2.在游戏结束前需要调用 quit 方法py
- 使用步骤大致分为两步,就不多废话第一步、修改hosts文件将0.0.0.0 account.jetbrains.com添加到hosts文件最
- leaflet为R语言提供了API很好用,这次尝试用Python使用leaflet,需要folium安装foliumpip install