前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Golang sync.Mutex 与 sync.RWMutex

Golang sync.Mutex 与 sync.RWMutex

作者头像
恋喵大鲤鱼
发布2019-06-14 20:43:59
1.8K0
发布2019-06-14 20:43:59
举报
文章被收录于专栏:C/C++基础

Golang中sync包实现了两种锁,Mutex(互斥锁)和RWMutex(读写锁),其中RWMutex是基于Mutex实现的。

1.sync.Mutex

代码语言:javascript
复制
type Mutex struct {
	// contains filtered or unexported fields
}

func (m *Mutex) Lock()
func (m *Mutex) Unlock()

sync.Mutex用于多个goroutine对共享资源的互斥访问。使用要点如下: (1)使用Lock()加锁,Unlock()解锁; (2)对未解锁的Mutex使用Lock()会阻塞; (3)对未上锁的Mutex使用Unlock()会导致 panic 异常。

多个goroutine对同一个全局变量读写需要加锁,示例如下:

代码语言:javascript
复制
package main

import (
    "fmt"
    "sync"
)

func main() {
        var intVar int
        var wg sync.WaitGroup
        var mutex sync.RWMutex
    go func(){
        defer wg.Done()
        mutex.Lock()
        intVar=4
        mutex.Unlock()
        fmt.Printf("first goroutine, intVar=%d\n",intVar)
        }()

    go func(){
        defer wg.Done()
        mutex.Lock()
        intVar=5
        mutex.Unlock()
        fmt.Printf("second goroutine, intVar=%d\n",intVar)
    }()

    wg.Add(2)
    wg.Wait()
    fmt.Println("end main goroutine")
}

输出结果:

代码语言:javascript
复制
second goroutine, intVar=5
first goroutine, intVar=4
end main goroutine

//或
first goroutine, intVar=4
second goroutine, intVar=5
end main goroutine

在解锁之前加锁会导致死锁。

代码语言:javascript
复制
package main

import (
    "fmt"
    "sync"
)

func main(){
    var mutex sync.Mutex
    mutex.Lock()
    fmt.Println("Locked")
    mutex.Lock()
}

输出结果:

代码语言:javascript
复制
Locked
fatal error: all goroutines are asleep - deadlock!

2.sync.RWMutex

代码语言:javascript
复制
type RWMutex struct {
	// contains filtered or unexported fields
}

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

sync.RWMutex用于读锁和写锁分开的情况。使用时注意如下几点: (1)RWMutex是单写多读锁,该锁可以加多个读锁或者一个写锁; (2)读锁占用的情况下会阻止写,不会阻止读,多个 goroutine 可以同时获取读锁; (3)写锁会阻止其他 goroutine(无论读和写)进来,整个锁由该 goroutine 独占; (4)适用于读多写少的场景。

2.1 Lock()与Unlock()

(1)Lock() 加写锁,Unlock() 解写锁 (2)如果在加写锁之前已经有其他的读锁和写锁,则 Lock() 会阻塞直到该锁可用,为确保该锁可用,已经阻塞的 Lock() 调用会从获得的锁中排除新的读取器,即写锁权限高于读锁,有写锁时优先进行写锁定; (3)在 Lock() 之前使用 Unlock() 会导致 panic 异常。

2.2 RLock() 和 RUnlock()

(1)RLock() 加读锁,RUnlock() 解读锁; (2)RLock() 加读锁时,如果存在写锁,则无法加读锁;当只有读锁或者没有锁时,可以加读锁,读锁可以加多个; (3)RUnlock() 解读锁,RUnlock() 撤销单次 RLock() 调用,对于其他同时存在的读锁则没有效果; (4)在没有读锁的情况下调用 RUnlock(),会导致 panic 错误; (5)RUnlock() 的个数不得多于 RLock(),否则会导致 panic 错误。

go map的读写是非原子操作,所以对于共享的map,如果有多个goroutine同时对map进行操作时,需要加读写锁。示例如下:

代码语言:javascript
复制
package main

import (
    "sync"
    "fmt"
)

func main() {
    mapNameAge :=make(map[string]int)
    var rwMutex sync.RWMutex
    var wg sync.WaitGroup
    go func(){
        defer wg.Done()
        rwMutex.Lock()
        mapNameAge["dablelv"]=18
        rwMutex.Unlock()
        fmt.Println("first goroutine to write map end")
    }()

    go func(){
        defer wg.Done()
        rwMutex.RLock()
        age, _ :=mapNameAge["dablelv"]
        rwMutex.RUnlock()
        fmt.Printf("second goroutine to read map end, map[dablelv]=%d\n",age)
    }()

    wg.Add(2)
    wg.Wait()
    fmt.Println("main goroutine end")
}

输出结果:

代码语言:javascript
复制
second goroutine to read map end, map[dablelv]=0
first goroutine to write map end
main goroutine end

//或
first goroutine to write map end
second goroutine to read map end, map[dablelv]=18
main goroutine end

2.3错误使用异常

(1)Unlock() 使用之前不存在 Lock()。

代码语言:javascript
复制
package main

import (
    "sync"
)

func main(){
    var rwMutex sync.RWMutex
    rwMutex.Unlock()
}

程序输出:

代码语言:javascript
复制
panic: sync: Unlock of unlocked RWMutex

(2)RUnlock() 之前不存在 RLock()。

代码语言:javascript
复制
package main

import (
    "sync"
)

func main(){
    var rwMutex sync.RWMutex
    rwMutex.RUnlock()
}

程序输出:

代码语言:javascript
复制
fatal error: sync: RUnlock of unlocked RWMutex

(3)RWMutex 使用不当导致的死锁。

代码语言:javascript
复制
package main

import (
    "sync"
)

func main(){
    var rwmutex *sync.RWMutex
    rwmutex = new(sync.RWMutex)
    rwmutex.Lock()
    rwmutex.Lock()
}

程序输出:

代码语言:javascript
复制
fatal error: all goroutines are asleep - deadlock!

(4)RUnlock() 个数多于 RLock()。

代码语言:javascript
复制
package main

import (
    "sync"
)

func main(){
    var rwmutex *sync.RWMutex
    rwmutex = new(sync.RWMutex)
    rwmutex.RLock()
    rwmutex.RLock()
    rwmutex.RUnlock()
    rwmutex.RUnlock()
    rwmutex.RUnlock()
}

程序输出:

代码语言:javascript
复制
panic: sync: RUnlock of unlocked RWMutex
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2019年05月23日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1.sync.Mutex
  • 2.sync.RWMutex
    • 2.1 Lock()与Unlock()
      • 2.2 RLock() 和 RUnlock()
        • 2.3错误使用异常
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档