Go 臨界資源的安全問題(引入同步異步處理)
作者:LiberHome
來源:SegmentFault 思否社區(qū)
臨界資源定義
并發(fā)環(huán)境中多個進程、線程、協(xié)程共享的資源
臨界資源的特點
可能會因為并發(fā)操作導(dǎo)致數(shù)據(jù)出現(xiàn)不一致性,舉個栗子,下面代碼中的a及時臨界資源
package main
import (
"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)
}
經(jīng)典的售票問題,好多個窗口同時售票的門票的數(shù)量就是一個典型的臨界資源安全問題,下面用4個協(xié)程模擬一下售票過程:
package main
import (
"fmt"
"math/rand"
"time"
)
var ticket = 10 //the amount of the total ticket is 100
func main() {
//這里啟動4個goroutine模擬4個售票口 同時售票
go saleTickets("ticket window1")
go saleTickets("ticket window2")
go saleTickets("ticket window3")
go saleTickets("ticket window4")
//這里為了保證 主協(xié)程 最后執(zhí)行完 先用sleep (當(dāng)然,也可以用同步等待組、chanel實現(xiàn))
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
}
}
}
運行結(jié)果如下:
ticket window3 saled: 10
ticket window1 saled: 9
ticket window4 saled: 8
ticket window2 saled: 7
ticket window3 saled: 6
ticket window2 saled: 5
ticket window1 saled: 4
ticket window1 saled: 3
ticket window4 saled: 2
ticket window2 saled: 1
ticket window2 sorry tickets are sold out
ticket window4 saled: 0
ticket window4 sorry tickets are sold out
ticket window3 saled: -1
ticket window3 sorry tickets are sold out
ticket window1 saled: -2
ticket window1 sorry tickets are sold out
我們假設(shè)現(xiàn)在只剩下最后1張票了,現(xiàn)在程序在主協(xié)程里面
窗口4的協(xié)程拿到cpu資源,讀取了剩余ticket總數(shù)為1,然后sleep,釋放cpu資源
窗口3的協(xié)程拿到cpu資源,發(fā)現(xiàn)剩余ticket總數(shù)為1(因為窗口4的協(xié)程進入sleep了,并沒有在窗口3拿到cpu資源之前對ticket進行修改),然后sleep,釋放cpu資源。
窗口4醒了,ticket = 1 - 1 = 0
窗口3醒了,ticket = 0 - 1 = -1
針對這種問題,可以通過上鎖,在某一時間段只允許一個goroutine來訪問這個共享數(shù)據(jù),訪問完畢,解鎖之后,其他goroutine才能訪問的方式解決。
不過有意思的是,go并不鼓勵這樣以共享的方式去通信,而是以通信的方式去共享【也就是不鼓勵用sync包上鎖,鼓勵使用chanel】

評論
圖片
表情
