Golang 并发和锁
Posted Harris-H
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Golang 并发和锁相关的知识,希望对你有一定的参考价值。
Golang 并发和锁
1.介绍
读写锁介绍
基本遵循两大原则:
1、可以随便读,多个goroutine同时读
2、写的时候,啥也不能干。不能读也不能写
RWMutex提供了四个方法:
func (*RWMutex) Lock // 写锁定
func (*RWMutex) Unlock // 写解锁
func (*RWMutex) RLock // 读锁定
func (*RWMutex) RUnlock // 读解锁
- 读锁的时候无需等待读锁的结束
- 读锁的时候要等待写锁的结束
- 写锁的时候要等待读锁的结束
- 写锁的时候要等待写锁的结束
其他介绍
golang中的锁分为互斥锁、读写锁、原子锁即原子操作。在 Golang 里有专门的方法来实现锁,就是 sync 包,这个包有两个很重要的锁类型。一个叫 Mutex, 利用它可以实现互斥锁。一个叫 RWMutex,利用它可以实现读写锁。
全局锁 sync.Mutex,是同一时刻某一资源只能上一个锁,此锁具有排他性,上锁后只能被此线程使用,直至解锁。加锁后即不能读也不能写。全局锁是互斥锁,即 sync.Mutex 是个互斥锁。
读写锁 sync.RWMutex ,将使用者分为读者和写者两个概念,支持同时多个读者一起读共享资源,但写时只能有一个,并且在写时不可以读。理论上来说,sync.RWMutex 的 Lock() 也是个互斥锁。
将上面的结论展开一下,更清晰得说(为避免理解偏差宁可唠叨一些):
sync.Mutex 的锁是不可以嵌套使用的。
sync.RWMutex 的 mu.Lock() 是不可以嵌套的。
sync.RWMutex 的 mu.Lock() 中不可以嵌套 mu.RLock()。(这是个注意的地方)
否则,会 panic fatal error: all goroutines are asleep - deadlock!
下面三种均会死锁
func lockAndRead1() // 全局锁内使用全局锁
l.Lock()
defer l.Unlock()
l.Lock()
defer l.Unlock()
func lockAndRead2() // 全局锁内使用可读锁
l.Lock()
defer l.Unlock() // 由于 defer 是栈式执行,所以这两个锁是嵌套结构
l.RLock()
defer l.RUnlock()
func lockAndRead3() // 可读锁内使用全局锁
l.RLock()
defer l.RUnlock()
l.Lock()
defer l.Unlock()
var l sync.RWMutex
func lockAndRead() // 可读锁内使用可读锁
l.RLock()
defer l.RUnlock()
l.RLock()
defer l.RUnlock()
func main()
lockAndRead()
time.Sleep(5 * time.Second)
上面这种方式不会嵌套。
2.读写锁实例
2.1 同时读
package main
import (
"sync"
"time"
)
var m *sync.RWMutex
func main()
m = new(sync.RWMutex)
// 多个同时读
go read(1)
go read(2)
time.Sleep(2*time.Second)
func read(i int)
println(i,"read start")
m.RLock()
println(i,"reading")
time.Sleep(1*time.Second)
m.RUnlock()
println(i,"read over")
结果
1 read start
1 reading
2 read start
2 reading
1 read over
2 read over
2.2 读写交替
package main
import (
"sync"
"time"
)
var m *sync.RWMutex
func main()
m = new(sync.RWMutex)
// 写的时候啥也不能干
go write(1)
go read(2)
go write(3)
time.Sleep(2*time.Second)
func read(i int)
println(i,"read start")
m.RLock()
println(i,"reading")
time.Sleep(1*time.Second)
m.RUnlock()
println(i,"read over")
func write(i int)
println(i,"write start")
m.Lock()
println(i,"writing")
time.Sleep(1*time.Second)
m.Unlock()
println(i,"write over")
结果
1 write start
1 writing
2 read start
3 write start
1 writing over
2 reading
2 read over
3 writing
3 write over
3.解决并发中goroutine未绑定变量
package main
import (
"fmt"
"time"
)
func main()
names := []string"niko","mike","tony"
for _,name := range names
go func (na string)
fmt.Printf("%s\\n",na)
(name)
time.Sleep(time.Second * 2)
4.互斥锁
互斥锁有两个方法:加锁、解锁。
一个互斥锁只能同时被一个 goroutine 锁定,其它 goroutine 将阻塞直到互斥锁被解锁(重新争抢对互斥锁的锁定)。使用Lock加锁后,不能再进行加锁,只有当对其进行Unlock解锁之后,才能对其加锁。这个很好理解。
如果对一个未加锁的资源进行解锁,会引发panic异常。
可以在一个goroutine中对一个资源加锁,而在另外一个goroutine中对该资源进行解锁。
不要在持有锁的时候做 IO 操作。尽量只通过持有锁来保护 IO 操作需要的资源而不是 IO 操作本身。
func (m *Mutex) Lock()
func (m *Mutex) Unlock()
5.锁拷贝问题
type MyMutex struct
count int
sync.Mutex
func main()
var mu MyMutex
mu.Lock()
var mu1 = mu
mu.count++
mu.Unlock()
mu1.Lock() //已经加锁,此时再加锁会死锁
mu1.count++
mu1.Unlock()
fmt.Println(mu.count, mu1.count)
加锁后复制变量,会将锁的状态也复制,所以 mu1 其实是已经加锁状态,再加锁会死锁
6.参考文章
以上是关于Golang 并发和锁的主要内容,如果未能解决你的问题,请参考以下文章