go 互斥锁,读写锁(sync.Mutex,sync.RWMutex)

Posted yanjoo

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了go 互斥锁,读写锁(sync.Mutex,sync.RWMutex)相关的知识,希望对你有一定的参考价值。

↑ 点击上方“yanjoo”关注我们


纠结了一下午的 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语言并发编程之互斥锁读写锁详解

Go-同步原语与锁互斥锁与读写锁

go中的sync.Mutex 和 sync.RWMutex

go中的sync.Mutex 和 sync.RWMutex

go语言学习笔记 — 进阶 — 并发编程:互斥锁(sync.Mutex)—— 保证同时只有一个goroutine可以访问共享资源

45. sync.Mutex 互斥和互斥锁 | 厚土Go学习笔记