共享资源竞争:
多个goroutine操作同一个共享数据,存在不安全。
i:=1
go func(){ //第一个Goroutine 我们叫他g1
if i>0{ //①第一次g1执行到这里的时候,资源被g2抢走
i--
fmt.Pringtln(i) //输出i=0
}
}// ③然后g1抢回资源 执行结束 此时i已经等于0
go func(){ //第二个Goroutine 我们叫他g2
if i>0{ //②g2执行到这里的时候又被g1把资源抢走了
i-- //④由于g1执行完毕 现在就剩下g2了 ,g2顺序执行,i--
fmt.Pringtln(i)//⑤ 此时输出i已经变成-1了,按理论来说已经不满足条件了,但是由于判断已经完成了,所以g2输出的是-1
}
}
一个共享数据比如i=1,被第一个Goroutien抢到了资源,条件是i>0,判断成立,下面执行i--,然后打印i,但是在i>0完之后又被第二个Goroutine抢到,同样执行判断i>0条件成立同样的 他也只是执行了条件判断,并没有执行i--和打印,这样就有两个满足条件的Goroutine了,第一个正常完成程序,正常输出i的值,但是第二个由于数据已经变了,所以输出的值是错误的。为了避免这种情况,我们就要给Goroutine加上互斥锁,在一个Goroutine访问数据时,别人是不能访问的,只有等访问完毕开锁之后,其他人才能来访问。
上锁:互斥锁(开,关)
sync.Mutex
Lock(),UnLock()
我们来举一个卖票的例子,一共100张票,4个窗口卖,每卖一张,总票数-1,如果在不上锁的情况下,有可能最后会出现多卖出3张的情况,为了更好的展现卖出的过程,我再里面加了睡眠。
package main
import (
"fmt"
"sync"
"math/rand"
"time"
)
//全局变量
var ticket2 = 10 // 100张票
var wg3 sync.WaitGroup
var matex sync.Mutex // 创建锁头
func main() {
/*
4个goroutine,模拟4个售票口,4个子程序操作同一个共享数据。
*/
wg3.Add(4)
go saleticket2s("售票口1") // g1,100
go saleticket2s("售票口2") // g2,100
go saleticket2s("售票口3") //g3,100
go saleticket2s("售票口4") //g4,100
wg3.Wait() // main要等待。。。
}
func saleticket2s(name string) {
rand.Seed(time.Now().UnixNano())
defer wg3.Done()
//for i:=1;i<=100;i++{
// fmt.Println(name,"售出:",i)
//}
for { //1, g1 ,g2,g3,g4
//上锁, g2
matex.Lock()
if ticket2 > 0 { //g1,g3,g2,g4
//睡眠
time.Sleep(time.Duration(rand.Intn(1000)) * time.Millisecond)
// g1 ,g3, g2,g4
fmt.Println(name, "售出:", ticket2) // 1 , 0, -1 , -2
ticket2-- //0 , -1 ,-2 , -3
} else {
matex.Unlock()//条件不满足,也要解锁,结束循环
fmt.Println("售罄,没有票了。。")
break
}
matex.Unlock()//解锁
}
}