临界资源定义
并发环境中多个过程、线程、协程共享的资源
临界资源的特点
可能会因为并发操作导致数据呈现不一致性,举个栗子,上面代码中的 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)
}
经典的售票问题,好多个窗口同时售票的门票的数量就是一个典型的临界资源平安问题,上面用 4 个协程模仿一下售票过程:
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")
// 这里为了保障 主协程 最初执行完 先用 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: 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
这里竟然卖出了 - 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