1. 程式人生 > >原 4.5-3 併發技術8:條件變數案例

原 4.5-3 併發技術8:條件變數案例

案例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)
}