互斥锁
应用场景
当多个groutine操作同一个线程不安全的公共资源时,为了安全我们使用互斥锁使得在某一时刻只有一个groutine操作该公共资源,即当有groutine在操作该共享变量(把共享变量加载到工作区,然后修改后再写入内存)时,其他线程在操作该共享变量时就会被block。
应用案例
我们对一个变量value=0进行1001000次+1的操作,我们知道正确答案应该是value=1001000=100000
当不使用互斥锁时
var wg *sync.WaitGroup=new(sync.WaitGroup)
var value int=0
wg.Add(100)
for i:=0;i<100;i++{
go func(){
for i:=0;i<1000;i++{
value++
}
defer wg.Done()
}()
}
wg.Wait()
fmt.Println(value)
打印出的结果为一个小于正确答案的随机值
当使用互斥锁将操作共享变量的操作上锁
var mutex sync.Mutex
var wg *sync.WaitGroup=new(sync.WaitGroup)
var value int=0
wg.Add(100)
for i:=0;i<100;i++{
go func(){
for i:=0;i<1000;i++{
mutex.Lock()
value++
mutex.Unlock()
}
defer wg.Done()
}()
}
wg.Wait()
fmt.Println(value)
打印出的结果为我们预期的100000
注意
lock与unlock操作一定要对应起来,如果在一个线程里面对同一个mutex多次进行lock而没有对应的unlock,那么就会死锁。如果在线程中对unlock过的mutex再unlock且缺乏对应的lock,则会引发一个不可恢复的恐慌。
读写锁
读写锁特点
读写锁中有两种锁即读锁和写锁,读锁之间两两互不干扰,写锁之间两两互斥,读锁和写锁互斥。写解锁会试图唤醒一个因欲进行读而被阻塞的线程,读解锁会试图唤醒一个因欲进行写而被阻塞的线程。若对一个未被 写/读 锁定的读写锁进行 写/读 解锁会引发一个不可恢复的恐慌。
应用情景
当一个公共资源同时被多个线程进行读和写的操作时,使用读写锁会更方便且开销更低。
应用案例
我们定义一个list类型为[]int,我们每隔0.5秒往list中添加一个数字1,并且读取当前list后一秒后再判断是原来读取的和一秒后的是否还是同一个list(我们用长度来判断)
不加读写锁
type List struct {
source []int
rwmutex sync.RWMutex
}
func(l *List)Read()[]int{
return l.source
}
func(l *List)Write(){
l.source=append(l.source,1)
}
func main() {
list:=List{}
list.source=[]int{}
for i:=0;i<3;i++{
go func(){
ticker:=time.NewTicker(time.Millisecond*500)
for{
list.Write()
<-ticker.C
}
}()
}
go func(){
ticker:=time.NewTicker(time.Second*1)
for{
)
oldlist:=list.Read()
<-ticker.C
newList:=list.Read()
fmt.Println(len(oldlist)==len(newList))
}
}()
time.Sleep(time.Second*7)
}
打印结果全为false,也就是读取的结果不能保证是最新的结果
加上读写锁
type List struct {
source []int
rwmutex sync.RWMutex
}
func(l *List)Read()[]int{
return l.source
}
func(l *List)Write(){
l.source=append(l.source,1)
}
func main() {
list:=List{}
list.source=[]int{}
for i:=0;i<3;i++{
go func(){
ticker:=time.NewTicker(time.Millisecond*500)
for{
list.rwmutex.Lock()
list.Write()
<-ticker.C
list.rwmutex.Unlock()
}
}()
}
go func(){
ticker:=time.NewTicker(time.Second*1)
for{
list.rwmutex.RLock()
oldlist:=list.Read()
<-ticker.C
newList:=list.Read()
fmt.Println(len(oldlist)==len(newList))
list.rwmutex.RUnlock()
}
}()
time.Sleep(time.Second*7)
}
打印的全为true,因为加上读写锁后,在读未完成新旧list对比之前往list添加数字的操作是阻塞的。