在編寫並發程式碼時,經常需要保證多個goroutine之間的同步和相互之間的令牌或鎖,以防止資料競爭和競態條件。 Go語言透過chan和sync套件提供了一些機制來實現這些同步,但有時候並不夠靈活,需要一些更高階的同步方法,這時候就需要用到屏障。
屏障是一種同步原語,它用於等待一組goroutine在同一時刻完成某項操作,然後再繼續執行下一步操作,這是一種非常常見的同步機制。在Golang中也提供了實現屏障的機制,即「Barrier(屏障)」。
屏障的原理是將一組goroutine分為兩個階段,第一階段在每個goroutine執行完自己的任務後停止,並等待其他goroutine完成,第二階段,當所有goroutine都已停止並互相設定訊號時,所有goroutine都同時恢復執行。
Golang在sync套件中提供了「WaitGroup」類型,使用Add方法來添加等待的goroutine數量,Done方法用於每個goroutine完成任務後向WaitGroup減少一個計數器,最後,Wait方法阻塞,直到計數器降為零,即所有goroutine都已完成任務,才會繼續執行下一步操作。
但是,WaitGroup有一個缺點,它只能等待一組固定數量的goroutine完成,當有新的goroutine加入時,我們就無法使用它了。這時候,我們可以使用Barrier來解決這個問題。
Barrier可以用於任意數量的goroutine,並且可以在每個階段結束時執行任意指定的操作。它包含一個計數器,表示有多少個goroutine正在等待該群組操作完成,以及一個函數,用於在每個階段結束時執行。
Golang中的“一組等待線程”,可以使用sync包的“WaitGroup(等待組)”來實現:
var wg sync.WaitGroup func worker() { defer wg.Done() // 执行自己的任务 } func main() { for i := 0; i < n; i++ { wg.Add(1) go worker() } // 等待所有线程完成 wg.Wait() }
現在,我們使用“Barrier”,創建一個可以應用於變化的goroutine的範例:
package main import "fmt" import "sync" func main() { b := sync.NewCond(&sync.Mutex{}) done := make(chan bool) worker := func(id int) { defer func() { fmt.Printf("Worker %d done\n", id) done <- true }() fmt.Printf("Worker %d processing\n", id) b.L.Lock() b.Wait() // 等待阻塞直到b.Broadcast()被执行 b.L.Unlock() fmt.Printf("Worker %d resumed\n", id) } go func() { for i := 0; i < 10; i++ { wg.Add(1) go worker(i) } }() go func() { for i := 0; i < 10; i++ { wg.Add(1) go worker(i) } }() go func() { wg.Wait() b.Broadcast() }() for i := 0; i < 20; i++ { <-done } fmt.Printf("All workers done\n") }
這個範例創建了20個goroutine,分別分為兩個階段,第一階段在每個goroutine完成自己的任務後等待,第二階段等待所有goroutine都完成後再執行操作。
當兩組goroutine被創建後,其中一個goroutine調用了「Wait」並阻塞,直到所有goroutine完成任務。使得其他執行緒都完成任務時,才釋放阻塞,並透過使用「Broadcast」向所有goroutine發送訊號,以繼續執行下一步操作。
總之,Golang的Barrier允許您更高級地同步並發操作,並使您的程式碼更健壯、可靠性更高。
以上是聊聊golang中的Barrier(屏障)的詳細內容。更多資訊請關注PHP中文網其他相關文章!