1.1 竞态
在串行程序中(即程序中只有一个goroutine),程序中各个步骤的执行顺序由程序逻辑来决定。
竞态是指在多个 goroutine 按照某些交错顺序执行时程序无法给出正确的结果。竞态对于程序是致命的,因为它们可能潜伏在程序中,出现频率很低,不易重现。
1.2 互斥锁:sync.Mutex
其中Mutex为互斥锁,Lock()加锁,Unlock()解锁,使用Lock()加锁后,便不能再次对其进行加锁,(如果再次加锁,就会造成死锁问题)。直到利用Unlock()解锁对其解锁后,才能再次加锁.适用于读写不确定场景,即读写次数没有明显的区别,并且只允许只有一个读或者写的场景,所以该锁叶叫做全局锁。
Unlock()用于解锁m,如果在使用Unlock()前未加锁,就会引起一个运行错误.已经锁定的Mutex并不与特定的goroutine相关联,这样可以利用一个goroutine对其加锁,再利用其他goroutine对其解锁。
import "sync"
var(
mu sync.Mtuex
balance int
)
func Deposit(amount int){
mu.lock()
balance += amount
mu.unlock()
}
func Balance() int {
mu.lock()
b := balance
mu.unlock()
return b
}
也可以使用同样的理念,使用一个容量为 1 缓冲通道来实现模拟一个互斥锁。
var(
mu := make(chan struct{}, 1)
balance int
)
func Depsit(amount int){
mu<- struct{}{} //获取令牌
balance += amount
<-mu //释放令牌
}
func Balance() int {
mu<- struct{}{} //获取令牌
b := amount
<- mu //释放令牌
return b
}
1.3 读写互斥锁:sync.RWMutex
RWMutex是一个读写锁,该锁可以加多个读锁或者一个写锁,其经常用于读次数远远多于写次数的场景.
写锁,如果在添加写锁之前已经有其他的读锁和写锁,则lock就会阻塞直到该锁可用,为确保该锁最终可用,已阻塞的 Lock 调用会从获得的锁中排除新的读取器,即写锁权限高于读锁,有写锁时优先进行写锁定
读锁,当有写锁时,无法加载读锁,当只有读锁或者没有锁时,可以加载读锁,读锁可以加载多个,所以适用于"读多写少"的场景
package main
import (
"fmt"
"sync"
"time"
)
var global int
var mutex sync.RWMutex
func Reader() {
mutex.RLock() //读锁
fmt.Printf("读到的是%d\n", global)
defer mutex.RUnlock() //释放读锁
}
func Write() {
mutex.Lock()
defer mutex.Unlock()
global += 3
}
func main() {
for x := 0; x < 100; x++ {
go Reader()
}
for x := 0; x < 100; x++ {
go Write()
}
for x := 0; x < 100; x++ {
go Write()
}
time.Sleep(10 * time.Second) //这里主 goroutine 要等待,不然副 goroutine 还没执行完,主 goroutine 退出,别的 goroutine 也就会退出
fmt.Printf("最后变量是%d", global) //最后输出的是 600 。
}