临界资源定义
并发环境中多个过程、线程、协程共享的资源
临界资源的特点
可能会因为并发操作导致数据呈现不一致性,举个栗子,上面代码中的a及时临界资源
package mainimport ( "fmt" "time")func main() { //临界资源 a := 1 go func() { a = 2 fmt.Println("in this goroutine: a is : ", a) }() //在主goroutine中 a = 3 time.Sleep(1) fmt.Println("in the main goroutine: a is : ", a)}
经典的售票问题,好多个窗口同时售票的门票的数量就是一个典型的临界资源平安问题,上面用4个协程模仿一下售票过程:
package mainimport ( "fmt" "math/rand" "time")var ticket = 10 //the amount of the total ticket is 100func main() { //这里启动4个goroutine模仿4个售票口 同时售票 go saleTickets("ticket window1") go saleTickets("ticket window2") go saleTickets("ticket window3") go saleTickets("ticket window4") //这里为了保障 主协程 最初执行完 先用sleep (当然,也能够用同步期待组、chanel实现) time.Sleep(10 * time.Second)}func saleTickets(name string) { rand.Seed(time.Now().UnixNano()) for { if ticket > 0 { time.Sleep(time.Duration(rand.Intn(1000)) * time.Millisecond) fmt.Println(name, "saled: ", ticket) ticket-- } else { fmt.Println(name, "sorry tickets are sold out") break } }}
运行后果如下:
ticket window3 saled: 10ticket window1 saled: 9ticket window4 saled: 8ticket window2 saled: 7ticket window3 saled: 6ticket window2 saled: 5ticket window1 saled: 4ticket window1 saled: 3ticket window4 saled: 2ticket window2 saled: 1ticket window2 sorry tickets are sold outticket window4 saled: 0ticket window4 sorry tickets are sold outticket window3 saled: -1ticket window3 sorry tickets are sold outticket window1 saled: -2ticket window1 sorry tickets are sold out
这里竟然卖出了-2张票,这显著除了问题,问题出在哪里呢?
- 咱们假如当初只剩下最初1张票了,当初程序在主协程外面
- 窗口4的协程拿到cpu资源,读取了残余ticket总数为1,而后sleep,开释cpu资源
- 窗口3的协程拿到cpu资源,发现残余ticket总数为1(因为窗口4的协程进入sleep了,并没有在窗口3拿到cpu资源之前对ticket进行批改),而后sleep,开释cpu资源。
- 窗口4醒了,ticket = 1 - 1 = 0
- 窗口3醒了,ticket = 0 - 1 = -1
针对这种问题,能够通过上锁,在某一时间段只容许一个goroutine来拜访这个共享数据,拜访结束,解锁之后,其余goroutine能力拜访的形式解决。
不过有意思的是,go并不激励这样以共享的形式去通信,而是以通信的形式去共享【也就是不激励用sync包上锁,激励应用chanel】
资源参考:bilibili