go语言并发之锁的基操
go语言除了使用goroutine和channel这种CSP模型,也支持传统的并发模型,今天我们就说一下go中的锁的使用。
互斥锁是传统并发对共享的资源进行访问空值的主要手段,在 sync 包中,Mutex 结构体表示。该结构体具备两个公共的方法,Lock和Unlock,前者锁定当前的互斥量,后者是解锁。这两个一定要一起使用,由于defer的存在,我们基本上是不会忘记解锁的,代码示例:
import "sync" func main(){ var m sync.Mutex m.Lock() defer m.Unlock() // do something }
互斥锁只能锁定一次,当在解锁之前再次进行加锁,便会死锁状态,如果在加锁前解锁,便会报错“panic: sync: unlock of unlocked mutex”
还有另外一个锁就是读写锁,顾名思义就是对读写操作的锁,与互斥锁的不同处主要是可以分别针对读写操作进行锁定和解锁。读写锁控制下,多个写操作是互斥的,读与写操作也是互斥的,但是多个读操作之间是不存在互斥的。在 sync 包中,RWMutex 结构体表示。
读写锁具有下面四个方法:
var m sync.RWMutex m.Lock() m.Unlock() m.RLock() m.RUnlock()
Lock和Unlock是对写操作的锁,RLock和RUnlock是对读操作的锁。
读写锁的写锁只能锁定一次,解锁前不能多次锁定,读锁可以多次,但读解锁次数最多只能比读锁次数多一次,一般情况下不建议读解锁次数多余读锁次数
在写代码时,如果map遇到了并发的写入数据,程序会panic的,所以对map的操作我们要考虑到并发,我们可以在map写入数据时对其加锁。
ma := make(map[string]string) m.Lock() ma["key"] = "value m.Unlock()
当然go语言给我们提供了一种同步的map,sync.Map,这个我们不需要加锁,只需要调用其已有的方法即可,具体内容不在这里实现,感兴趣的可以看一下同步map的使用。
还有一个就是sync包中的Once,在我之前的单例模式的文章中说过,这个是只会执行一次,所以我们基本上都是在这里面做只需要一次的初始化的操作,当然这个也是go语言实现单例模式(对设计模式感兴趣的同学可以看我之前的文章)的不二选择。
var ma map[string]string var once sync.Once once.Do(func() { ma = make(map[string]string) })
后续会有更多的模式和算法以及区块链相关的,如果你是想学习go语言或者是对设计模式或者算法感兴趣亦或是区块链开发工作者,都可以关注一下。(微信公众号:Go语言之美,更多go语言知识信息等)。公众号会持续为大家分享更多干货。