sync.WaitGroup和sync.Once的愛恨情仇
今天,我們將繼續探討Go語言中的兩個重要的同步工具:sync.WaitGroup 和 sync.Once。
sync.WaitGroup
sync.WaitGroup 是Go語言中的一種計數信號量,用于等待一組 goroutine 完成。它常用于等待一組并發任務全部完成后再繼續執行。
使用方法
- 聲明一個 sync.WaitGroup 類型的變量。
- 在每個 goroutine 啟動之前調用 Add 方法,增加等待計數。
- 在每個 goroutine 完成時調用 Done 方法,減少等待計數。
- 在主 goroutine 中調用 Wait 方法,阻塞直到所有 goroutine 完成。
示例代碼
package main
import (
"fmt"
"sync"
"time"
)
func worker(id int, wg *sync.WaitGroup) {
defer wg.Done()
fmt.Printf("Worker %d starting\n", id)
time.Sleep(time.Second)
fmt.Printf("Worker %d done\n", id)
}
func main() {
var wg sync.WaitGroup
for i := 1; i <= 5; i++ {
wg.Add(1)
go worker(i, &wg)
}
wg.Wait()
fmt.Println("All workers done")
}
在這個例子中,main 函數啟動了5個 goroutine,每個 goroutine 都會運行 worker 函數。每個 worker 在完成時調用 wg.Done(),而 main 函數會等待所有 worker 完成后再繼續執行。
注意事項
- WaitGroup 的計數器不能設為負數,否則會引發 panic。
- 必須確保在所有 Done 調用之前已經調用了 Add。
sync.Once
sync.Once 是一個用于確保某些操作只執行一次的結構體。它提供了一種線程安全的方式來執行一次性初始化操作。
使用方法
- 聲明一個 sync.Once 類型的變量。
- 使用 Do 方法執行需要僅執行一次的操作。
示例代碼
package main
import (
"fmt"
"sync"
)
func initialize() {
fmt.Println("Initializing...")
}
func main() {
var once sync.Once
for i := 0; i < 10; i++ {
go func(i int) {
once.Do(initialize)
fmt.Printf("Goroutine %d\n", i)
}(i)
}
// 等待所有 goroutine 完成
var wg sync.WaitGroup
wg.Add(10)
for i := 0; i < 10; i++ {
go func() {
defer wg.Done()
once.Do(initialize)
}()
}
wg.Wait()
}
在這個例子中,initialize 函數只會被執行一次,盡管有多個 goroutine 嘗試調用 once.Do(initialize)。
注意事項
- sync.Once 的 Do 方法接受一個無參函數。
- 即使 Do 方法被多次調用,傳入的函數也只會執行一次。
結合使用示例
我們可以結合 sync.WaitGroup 和 sync.Once,來完成一個更復雜的并發任務。假設我們有一個初始化操作,只需執行一次,但在多個 goroutine 中執行其他任務。
示例代碼
package main
import (
"fmt"
"sync"
"time"
)
var (
once sync.Once
wg sync.WaitGroup
)
func initialize() {
fmt.Println("Initializing...")
time.Sleep(2 * time.Second) // 模擬初始化耗時
fmt.Println("Initialization complete")
}
func worker(id int) {
defer wg.Done()
once.Do(initialize)
fmt.Printf("Worker %d starting\n", id)
time.Sleep(time.Second) // 模擬工作
fmt.Printf("Worker %d done\n", id)
}
func main() {
const numWorkers = 5
wg.Add(numWorkers)
for i := 1; i <= numWorkers; i++ {
go worker(i)
}
wg.Wait()
fmt.Println("All workers done")
}
在這個例子中,initialize 函數只會執行一次,而 worker 函數會并發執行,等待所有 worker 完成后,程序才會繼續執行。
總結
通過本文,我們了解了Go語言中的兩個重要同步工具:sync.WaitGroup 和 sync.Once。sync.WaitGroup 用于等待一組 goroutine 完成,而 sync.Once 則確保某些操作只執行一次。這兩個工具在實際開發中非常實用,能有效地幫助我們處理并發任務。