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

type Mutex

    func (m *Mutex) Lock()
    func (m *Mutex) Unlock()
type RWMutex
    func (rw *RWMutex) Lock()
    func (rw *RWMutex) RLock()
    func (rw *RWMutex) RLocker() Locker
    func (rw *RWMutex) RUnlock()

func (rw *RWMutex) Unlock()

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

func (m *Mutex) Unlock()用于解锁m,如果在使用Unlock()前未加锁,就会引起一个运行错误.

已经锁定的Mutex并不与特定的goroutine相关联,这样可以利用一个goroutine对其加锁,再利用其他goroutine对其解锁.

正常运行例子:

package main

import (
"fmt"
"sync"
) func main() {
var l *sync.Mutex
l = new(sync.Mutex)
l.Lock()
defer l.Unlock()
fmt.Println("1")
}
结果输出:1

当Unlock()在Lock()之前使用时,便会报错

package main

import (
"fmt"
"sync"
) func main() {
var l *sync.Mutex
l = new(sync.Mutex)
l.Unlock()
fmt.Println("1")
l.Lock()
}
运行结果: panic: sync: unlock of unlocked mutex

当在解锁之前再次进行加锁,便会死锁状态

package main

import (
"fmt"
"sync"
) func main() {
var l *sync.Mutex
l = new(sync.Mutex)
l.Lock()
fmt.Println("1")
l.Lock()
}
运行结果:  1
 
  fatal error: all goroutines are asleep - deadlock!

RWMutex是一个读写锁,该锁可以加多个读锁或者一个写锁,其经常用于读次数远远多于写次数的场景.

func (rw *RWMutex) Lock()  写锁,如果在添加写锁之前已经有其他的读锁和写锁,则lock就会阻塞直到该锁可用,为确保该锁最终可用,已阻塞的 Lock 调用会从获得的锁中排除新的读取器,即写锁权限高于读锁,有写锁时优先进行写锁定
  func (rw *RWMutex) Unlock() 写锁解锁,如果没有进行写锁定,则就会引起一个运行时错误.

package main

import (
"fmt"
"sync"
) func main() {
var l *sync.RWMutex
l = new(sync.RWMutex)
l.Unlock()
fmt.Println("1")
l.Lock()
}
运行结果:panic: sync: unlock of unlocked mutex

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

func (rw *RWMutex)RUnlock() 读锁解锁,RUnlock 撤销单次 RLock 调用,它对于其它同时存在的读取器则没有效果。若 rw 并没有为读取而锁定,调用 RUnlock 就会引发一个运行时错误(注:这种说法在go1.3版本中是不对的,例如下面这个例子)。

package main

import (
"fmt"
"sync"
) func main() {
var l *sync.RWMutex
l = new(sync.RWMutex)
l.RUnlock()    //1个RUnLock
fmt.Println("1")
l.RLock()
} 运行结果:1
但是程序中先尝试 解锁读锁,然后才加读锁,但是没有报错,并且能够正常输出.

分析:go1.3版本中出现这种情况的原因分析,通过阅读源码可以很清晰的得到结果

func (rw *RWMutex) RUnlock() {
if raceenabled {
_ = rw.w.state
raceReleaseMerge(unsafe.Pointer(&rw.writerSem))
raceDisable()
}<span style="color:#FF0000;">
if atomic.AddInt32(&rw.readerCount, -1) < 0 { //readercounter初始值为0,调用RUnLock之后变为-1,继续往下执行
// A writer is pending.
if atomic.AddInt32(&rw.readerWait, -1) == 0 { //此时readerwaiter变为1,1-1之后变为0,可以继续以后的操作.</span>
// The last reader unblocks the writer.
runtime_Semrelease(&rw.writerSem)
}
}
if raceenabled {
raceEnable()
}
}

当RUnlock多于RLock多个时,便会报错,进入死锁.实例如下:

package main

import (
"fmt"
"sync"
) type s struct {
readerCount int32
} func main() {
l := new(sync.RWMutex)
l.RUnlock()
l.RUnlock()        //此处出现死锁
fmt.Println("1")
l.RLock()
}
运行结果:
1
 
  fatal error: all goroutines are asleep - deadlock!

总结:

所以在go1.3版本中,运行过程中允许RUnLock早于RLock一个,也只能早于1个(注:虽然代码允许,但是强烈不推荐使用),并且在早于之后必须利用RLock进行加锁才可以继续使用.

golang中sync.RWMutex和sync.Mutex区别的更多相关文章

  1. golang中,new和make的区别

    在golang中,make和new都是分配内存的,但是它们之间还是有些区别的,只有理解了它们之间的不同,才能在合适的场合使用. 简单来说,new只是分配内存,不初始化内存: 而make即分配又初始化内 ...

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

    前言 Golang中有两种类型的锁,Mutex (互斥锁)和RWMutex(读写锁)对于这两种锁的使用这里就不多说了,本文主要侧重于从源码的角度分析这两种锁的具体实现. 引子问题 我一般喜欢带着问题去 ...

  3. 【GoLang】golang中 channel 实现同步 与mutex/atomic 实现同步的讨论

    参考资料: https://groups.google.com/forum/#!topic/golang-china/q4pFH-AGnfs

  4. golang中锁

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

  5. golang 中 sync.Mutex 的实现

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

  6. golang中并发sync和channel

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

  7. Go 互斥锁(sync.Mutex)和 读写锁(sync.RWMutex)

    什么时候需要用到锁? 当程序中就一个线程的时候,是不需要加锁的,但是通常实际的代码不会只是单线程,所以这个时候就需要用到锁了,那么关于锁的使用场景主要涉及到哪些呢? 多个线程在读相同的数据时 多个线程 ...

  8. golang 中 sync包的 WaitGroup

    golang 中的 sync 包有一个很有用的功能,就是 WaitGroup 先说说 WaitGroup 的用途:它能够一直等到所有的 goroutine 执行完成,并且阻塞主线程的执行,直到所有的 ...

  9. golang sync.RWMutex

    sync.RWMutex package main import ( "fmt" "runtime" "sync" ) func click ...

随机推荐

  1. 【CSS】div

    一.div內容溢出 .remark-div { overflow: auto; height: auto; max-height: 100px; } 1.溢出 overflow :auto时,内容超过 ...

  2. linux 安装gcc 和 g++

    以CentOS为例,安装后是没有C语言和C++编译环境的,需要手动安装,最简单的是用yum的方式安装,过程如下: 1.安装gcc yum install gcc 询问是否,按y键回车即可,或者 yum ...

  3. 2017.10.6 国庆清北 D6T1 排序

    题目描述 小Z 有一个数字序列a1; a2; .... ; an,长度为n,小Z 只有一个操作:选 定p(1<p<n),然后把ap 从序列中拿出,然后再插⼊到序列中任意位置. 比如a 序列 ...

  4. neo4j基础操作

    删除所有节点 MATCH (n) OPTIONAL MATCH (n)-[r]-() DELETE n,r 创建节点 CREATE(:person{name:'潘峰',age:27}) CREATE( ...

  5. 通过 frp 实现群晖的 drive 远端同步

    通过 frp 实现 drive 同步 其实其他的也类似, 只是指定 tcp 的端口不一致就可以. frp 实现的外网端口映射"肥肠"方便. 也推广下 frp 的地址 frp-git ...

  6. Sphinx全文索引引擎

    一.什么是sphinx 原理:sphinx将数据库中的表建立索引,php操作sphinx时,将要查询的关键字进行匹配,返回一个id,php通过id到数据库中查询数据. 二.下载 链接:https:// ...

  7. iOS开发之如何在用户删除应用后保持一些数据

    在开发过程中我们有时候在用户删除时候保存一些信息在用户下次安装应用时候使用,这个时候我们可以使用剪切版UIPasteboard的FindUIPasteboard和钥匙串keychain的使用 剪切版剪 ...

  8. 微软校园招聘  研发工程师A

    1.const A. const int a; B. int const a; a是常数 C. int const *a; D. const int *a; 常量指针,指向一个常量的指针 E. int ...

  9. QT中显示动画

    在QT中要显示GIF图片,不能通过单单的添加部件来完成.还需要手动的编写程序.工具:QT Creator新建一个工程,我们先在designer中,添加一个QLabel部件. 将QLabel拉成适当大小 ...

  10. comparison of truncate vs delete in mysql/sqlserver

    comparison of truncate vs delete in mysql/sqlserver [duplicate]   DELETE DELETE is a DML Command. DE ...