原 4.5-3 併發技術8:條件變數案例
阿新 • • 發佈:2019-01-13
案例1:伺服器負載控制
- 監聽最大客戶端連線數
- 服務端協程:只要伺服器過載,就通知控制協程,並進入阻塞等待
- 控制協程:受到服務端預警,削減客戶端數量,並通知服務端(預警已解除)
package main import ( "fmt" "math/rand" "sync" "time" ) /* 伺服器負載控制 ·監聽最大客戶端連線數 ·服務端協程:只要伺服器過載,就通知控制協程,並進入阻塞等待 ·控制協程:受到服務端預警,削減客戶端數量,並通知服務端(預警已解除) */ type Server struct { maxConnections int connections int chAlarm chan bool cond *sync.Cond } /*服務端工廠方法*/ func NewServer(maxConnections int) *Server { server := new(Server) //初始化最大連線數 server.maxConnections = maxConnections //初始化條件變數 server.cond = sync.NewCond(&sync.Mutex{}) //初始化預警管道 server.chAlarm = make(chan bool,0) return server } /*啟動過載處理器*/ func (s *Server) StartOverloadHandler() { fmt.Println("StartOverloadHandler...") go func() { for { //阻塞接收預警訊息 <-s.chAlarm fmt.Println("已收到負載預警") //加鎖削減客戶端數量,併發送預警解除廣播 s.cond.L.Lock() <-time.After(3 * time.Second) s.connections -= rand.Intn(s.maxConnections) fmt.Println("s.connections=", s.connections) //傳送預警解除通知(會通知到Wait中的服務端主程式) s.cond.Signal() fmt.Println("過載預警已解除") s.cond.L.Unlock() } }() } func (s *Server) Run() { for { //加鎖檢測是否過載 s.cond.L.Lock() /*只要過載,就傳送預警,並釋放資源鎖並阻塞等待*/ for s.connections >= s.maxConnections { //傳送預警 s.chAlarm <- true fmt.Println("過載預警已傳送") //阻塞等待預警解除 s.cond.Wait() } //接入客戶端 time.Sleep(1 * time.Second) s.connections ++ fmt.Println("已接入客戶端:s.connections=", s.connections) s.cond.L.Unlock() } } func main() { server := NewServer(5) //服務端跑起負載處理器 server.StartOverloadHandler() //執行伺服器主程式 server.Run() }
案例2:城管來了
- 多個燒烤攤主協程監聽城管大隊的動向
- 只要出動就發訊息通知工會主席,並進入阻塞等待至被喚醒,否則就提供露天燒烤
- 工會主席協程:收到燒烤攤主們的訊息後,出面擺平城管大隊,並廣播通知所有燒烤攤主恢復營業
- 這裡的廣播我們使用了一對多的廣播:cond.Broadcast()
程式碼實現
package main import ( "fmt" "sync" "time" ) /* 城管預警 ·監聽城管大隊 ·燒烤攤叢集: 監聽城管大隊, 只要出動就發訊息通知工會主席並進入阻塞等待至被喚醒 否則就提供露天燒烤 ·工會主席:擺平城管大隊,並廣播通所有燒烤攤主 */ func main() { var dangerous = false cond := sync.NewCond(&sync.Mutex{}) chDanger := make(chan string, 1) for i := 1; i <= 3; i++ { go func(index int) { for { //只要城管出來,就等待起來 cond.L.Lock() for dangerous == true { //傳送預警() select { case chDanger <- "城管來了!!!": fmt.Println(index,"城管來了!!!") fmt.Println(index, ":進入蟄伏狀態") default: //已經有人發過了 } fmt.Println(index, ":進入蟄伏狀態") cond.Wait() } cond.L.Unlock() //城管沒有出來 fmt.Println(index, ":提供露天燒烤...") time.Sleep(3 * time.Second) } }(i) } go func() { for { select { case <-chDanger: //幫大家平事 cond.L.Lock() time.Sleep(3 * time.Second) dangerous = false cond.Broadcast()//喚醒所有人 fmt.Println("事情已經擺平") cond.L.Unlock() default: //日常生活 fmt.Println("工會主席的日常幸福生活...") dangerous = true time.Sleep(7 * time.Second) } } }() time.Sleep(365 * time.Second) }