一、什么场景下需要用到锁
当程序中就一个线程的时候,是不需要加锁的,但是通常实际的代码不会只是单线程,有可能是多个线程同时访问公共资源,所以这个时候就需要用到锁了,那么关于锁的使用场景主要涉及到哪些呢?

1. 多个线程在读相同的数据时
2. 多个线程在写相同的数据时
3. 同一个资源,有读又有写时

二、Go中锁分为两种:
互斥锁 (sync.Mutex)
读写锁 (sync.RWMutex 底层依赖Mutex实现 )
互斥锁是并发程序对公共资源访问限制最常见的方式。在Go中,sync.Mutex 提供了互斥锁的实现。

当一个goroutine获得了Mutex后,其他goroutine只能等待,除非该goroutine释放这个Mutex。

互斥锁结构:

type Mutex struct {
state int32
sema uint32
}
1. 锁定状态值为1,未锁定状态锁未0 。

2. Lock()加锁、Unlock解锁。

读写锁则是对读写操作进行加锁。需要注意的是多个读操作之间不存在互斥关系,这样提高了对共享资源的访问效率。

Go中读写锁由 sync.RWMutex 提供,RWMutex在读锁占用的情况下,会阻止写,但不阻止读。RWMutex在写锁占用情况下,会阻止任何其他goroutine(无论读和写)进来,整个锁相当于由该goroutine独占。

读写锁结构:

type RWMutex struct {
w Mutex // held if there are pending writers
writerSem uint32 // semaphore for writers to wait for completing readers
readerSem uint32 // semaphore for readers to wait for completing writers
readerCount int32 // number of pending readers
readerWait int32 // number of departing readers
}
1. RWMutex是单写多读锁,该锁可以加多个读锁或者一个写锁。

2. 读锁占用的情况会阻止写,不会阻止读,多个goroutine可以同时获取读锁。

3. 写锁会阻止其他gorotine不论读或者写进来,整个锁由写锁goroutine占用 与第一条共用示范代码

4. 适用于读多写少的场景

三、如何使用互斥锁
Mutex为互斥锁,Lock() 加锁,Unlock() 解锁,使用Lock() 加锁后,便不能再次对其进行加锁,直到利用Unlock()解锁对其解锁后,才能再次加锁.适用于读写不确定场景,即读写次数没有明显的区别,并且只允许只有一个读或者写的场景,所以该锁叶叫做全局锁。

互斥锁只能锁定一次,当在解锁之前再次进行加锁,便会无法加锁。如果在加锁前解锁,便会报错"panic: sync: unlock of unlocked mutex"。

package main

import (
"fmt"
"sync"
) var (
count int
mutex sync.Mutex
) func main() {
for i := 0; i < 2; i++ {
go func() {
for i := 1000000; i >0; i-- {
mutex.Lock()
count++
mutex.Unlock()
}
fmt.Println(count)
}()
} fmt.Scanf("请输入:【exit】 结束")
}

  

运行结果:
1952533
2000000 //最后的线程打印输出
对于上面的程序,a作为一个公共的资源,所以对a的改变、读写等操作都需要加锁。

需要注意的问题:

  1. 不要重复锁定互斥锁
  2. 不要忘记解锁互斥锁,必要时使用 defer 语句
  3. 不要在多个函数之间直接传递互斥锁

四、如何使用读写锁
读写锁的场景主要是在多线程的安全操作下,并且读的情况多于写的情况,也就是说既满足多线程操作的安全性,也要确保性能不能太差,这时候,我们可以考虑使用读写锁。当然你也可以简单暴力直接使用互斥锁(Mutex)。

Lock() 写锁,如果在添加写锁之前已经有其他的读锁和写锁,则lock就会阻塞直到该锁可用,为确保该锁最终可用,已阻塞的 Lock 调用会从获得的锁中排除新的读取器,即写锁权限高于读锁,有写锁时优先进行写锁定。

Unlock() 写锁解锁,如果没有进行写锁定,则就会引起一个运行时错误。

RLock() 读锁,当有写锁时,无法加载读锁,当只有读锁或者没有锁时,可以加载读锁,读锁可以加载多个,所以适用于"读多写少"的场景。

RUnlock() 读锁解锁,RUnlock 撤销单次RLock 调用,它对于其它同时存在的读取器则没有效果。若 rw 并没有为读取而锁定,调用 RUnlock 就会引发一个运行时错误。

package main

import (
"fmt"
"sync"
) var (
count int
rwMutex sync.RWMutex
) func main() {
for i := 0; i < 2; i++ {
go func() {
for i := 1000000; i >0; i-- {
rwMutex.Lock()
count++
rwMutex.Unlock()
}
fmt.Println(count)
}()
} fmt.Scanf("请输入:【exit】 结束")
}

  

运行结果:
1968637
2000000
看着挺复杂的,其实简单来说就是:

读锁不能阻塞读锁

读锁需要阻塞写锁,直到所有读锁都释放

写锁需要阻塞读锁,直到所有写锁都释放

写锁需要阻塞写锁

golang中锁的更多相关文章

  1. golang中锁mutex的实现

    golang中的锁是通过CAS原子操作实现的,Mutex结构如下: type Mutex struct {     state int32                     sema  uint ...

  2. golang 互斥锁和读写锁

    golang 互斥锁和读写锁 golang中sync包实现了两种锁Mutex(互斥锁)和RWMutex(读写锁),其中RWMutex是基于Mutex实现的,只读锁的实现使用类似引用计数器的功能. ty ...

  3. golang中并发sync和channel

    golang中实现并发非常简单,只需在需要并发的函数前面添加关键字"go",但是如何处理go并发机制中不同goroutine之间的同步与通信,golang 中提供了sync包和channel ...

  4. golang中sync.RWMutex和sync.Mutex区别

    golang中sync包实现了两种锁Mutex (互斥锁)和RWMutex(读写锁),其中RWMutex是基于Mutex实现的,只读锁的实现使用类似引用计数器的功能. type Mutex     f ...

  5. 【协作式原创】查漏补缺之Golang中mutex源码实现(预备知识)

    预备知识 CAS机制 1. 是什么 参考附录3 CAS 是项乐观锁技术,当多个线程尝试使用 CAS 同时更新同一个变量时,只有其中一个线程能更新变量的值,而其它线程都失败,失败的线程并不会被挂起,而是 ...

  6. 【协作式原创】查漏补缺之Golang中mutex源码实现

    概览最简单版的mutex(go1.3版本) 预备知识 主要结构体 type Mutex struct { state int32 // 指代mutex锁当前的状态 sema uint32 // 信号量 ...

  7. golang中mutex使用

    package main import ( "fmt" "sync" "time" ) /* 高并发是golang语言最大的亮点 一个线程可 ...

  8. golang 中 sync.Mutex 的实现

    mutex 的实现思想 mutex 主要有两个 method: Lock() 和 Unlock() Lock() 可以通过一个 CAS 操作来实现 func (m *Mutex) Lock() { f ...

  9. 延宕执行,妙用无穷,Go lang1.18入门精炼教程,由白丁入鸿儒,Golang中defer关键字延迟调用机制使用EP17

    先行定义,延后执行.不得不佩服Go lang设计者天才的设计,事实上,defer关键字就相当于Python中的try{ ...}except{ ...}finally{...}结构设计中的finall ...

随机推荐

  1. C++使用C语言库函数创建文件夹

    概述 本文演示环境: win10 + vs2017 头文件 #include <io.h> #include <direct.h> 函数 下面的函数,从左至右依次检查文件夹是否 ...

  2. 【LeetCode】198. House Robber 打家劫舍 解题报告(Java & Python)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 递归 递归 + 记忆化 动态规划 优化动态规划空间 ...

  3. 1030 - Discovering Gold

    1030 - Discovering Gold    PDF (English) Statistics Forum Time Limit: 2 second(s) Memory Limit: 32 M ...

  4. <学习opencv>opencv函数

    /*=========================================================================*/ // openCV中的函数 /*====== ...

  5. Linux_Vmtools的重安装与设置共享文件夹

    前置准备 已经安装了Linux的Vm虚拟机 2. 虚拟机上已经安装gcc 重装Vmtools Part1 用root账号登录Linux 弹出原来cd Vm菜单栏 - 虚拟机(M) - 重新安装VmWa ...

  6. Java代码实体类生成SQL语句(Java实体类转数据库)

    有的时候把数据库删了,如果照着实体类重新创建数据库的话比较麻烦,可以使用这个工具,把代码复制到项目里面设置一下即可把Java代码中的实体类转换为SQL语句输出为一个文件,打开执行命令即可. 下载:ht ...

  7. [学习笔记] Oracle运算符、连接符、结果去重、范围查询、模糊查询

    运算符 符号 解释 + 加法 - 减法 * 乘法 / 除法,结果是浮点数 = 等于 > 大于 < 小于 <>或者!= 不等于 >= 大于或者等于 <= 小于或者等于 ...

  8. Python+flask+flask-apscheduer实现定时下发任务

    Python+flask+flask-apscheduer实现定时下发任务 背景: 使用python+flask+mamaca实现的自动化用例管理平台,可以下发任务到具体的节点,进行执行测试用例,没有 ...

  9. PowerShell 之常用方法

    随笔分类 - 常用 [PowerShell]文件的解压与压缩 摘要:1 New-Item -ItemType File 1.txt -Force #新建文本文件 2 Compress-Archive ...

  10. alias 中使用 awk

    alias hehistory10='history |awk "{print \$2}"|sort|uniq -c|sort -rn|head -10' alias lv='ls ...