go 互斥锁,读写锁(sync.Mutex,sync.RWMutex)
Posted yanjoo
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了go 互斥锁,读写锁(sync.Mutex,sync.RWMutex)相关的知识,希望对你有一定的参考价值。
纠结了一下午的 GO 读写锁,总结一下。 go 的并发很简单,但用起来可能会比较纠结。
程序并发,就不得不涉及到锁,因为资源的内存是同一块,但是却有多个程序共同操作这块内存,结果就会混乱,所以当一个程序执行的时候,就需要一个锁来锁住资源,不让其他的程序访问。(多用于多线程,多进程)
在 GO 里有两种锁,互斥锁和读写锁。
互斥锁(sync.Mutex)
互斥锁在 GO 里用起来比较简单也比较方便。 用个抢购例子来说一下:
比如抢购数量是100;
var total int = 100 // 库存
func read() int{ // 读取库存
fmt.Println("total:%d",total)
return total
}
func buy(){ // 减少库存
total = total - 1
fmt.Println("stock:%d",total)
}
func calc(){
stock := read()
if(stock>0){
buy()
}
}
func main() {
calc()
}
这段代码很简单,如果单线程执行也是没有问题的。商品被买走一个,库存就减少一个。但是如果并发执行,就出问题了,如果到了临界点,库存还剩 1 个,这时候,100多个用户都在同时抢最后一个商品,他们读取的库存都是 1 ,都符合购买的条件,然后他们都一起购买了,库存变成 -99 ,商店去哪里给客户找商品,如果要是1000人,10000人抢呢,悲剧了。哈哈
所以,我们需要在用户购买的过程加锁,只能一个用户操作,其他用户等待,这样,就不会出现上述的情况。将 calc 函数改造一下,像下面一样加上互斥锁,这样,一个用户访问的时候,别的用户就不能访问库存资源了。只有等当前用户读取,操作了库存之后,才能访问。
var lock sync.Mutex
func calc(){
lock.Lock()
stock := read()
if(stock>0){
buy()
}
lock.Unlock()
}
如果想看到效果,可以在 main 多开些协程,达到多用户抢购的效果。
func main() {
for i := 0; i < 50; i++ {
go calc()
}
}
可以试试把 calc 里的互斥锁去掉,看看什么效果。
读写锁(sync.RWMutex)
读写锁暂时没有什么好例子,哈哈,简单的说一下读写锁的用法。
sync.RWMutex
RLock() // 读锁锁定
RUnlock() // 读锁解锁定
Lock() // 写锁锁定
Unlock() // 写锁解锁定
为什么有互斥锁还需要读写锁呢,互斥锁的控制面太大,不够细。互斥锁加上后,别人不能写,也不能读。比如在读数据的时候,加锁。这时候其他用户读取数据对数据没什么影响。如果加互斥锁锁住,其他的用户也不能读取,资源浪费了。
读写锁里的读锁锁定,其他N多的用户读取不影响,但是不能写入。只有等待读锁解锁,才能写入。避免用户读取数据的时候,数据已经被别的用户改变。所谓一写多读。
写锁定锁定的话,和互斥锁一样,其他用户不能读不能写。下面用一个城市信息的 map 来看一下。
type citys struct{
rw *sync.RWMutex
data map[interface{}]interface{}
}
func (c citys) readCitys() {
c.rw.RLock()
fmt.Println("read lock begin")
for ci := range c.data{
fmt.Println("city :",ci,c.data[ci])
}
c.rw.RUnlock()
}
func (c citys) writeCitys(key interface{},cityName interface{}) {
c.rw.Lock()
defer c.rw.Unlock()
c.data[key] = cityName
}
func (c citys) Each(cb func(interface{},interface{})) {
c.rw.RLock()
defer c.rw.RUnlock()
for key,val:= range c.data{
cb(key,val)
}
}
func main() {
t := citys{
rw:new(sync.RWMutex),
data:make(map[interface{}]interface{}),
}
t.data["ch"] = "china"
t.data["be"] = "beijing"
t.data["hu"] = "shanghai"
t.data["ji"] = "hebei"
t.data["yu"] = "henan"
t.data["lu"] = "shandong"
tt := citys{
rw:new(sync.RWMutex),
data:make(map[interface{}]interface{}),
}
for i := 0; i < 10; i++ {
go func() {
tt.readCitys()
}()
go func() {
t.Each(func(k interface{},v interface{}) {
tt.writeCitys(k,k) // fmt.Println(k,"is ",v)
})
}()
}
select{}
}
citys 读取的时候,其他人也可以读取,但是不能被写入。写入的时候,其他人不能读取也不能写入。
编程学习,资讯,技术分享php,PYTHON,GO,C/C++,LINUX,mysql,nginx,mongodb,redis,memcached,sphinx,android,ios
yanjoo
以上是关于go 互斥锁,读写锁(sync.Mutex,sync.RWMutex)的主要内容,如果未能解决你的问题,请参考以下文章
go语言学习笔记 — 进阶 — 并发编程:互斥锁(sync.Mutex)—— 保证同时只有一个goroutine可以访问共享资源