sync.WaitGroup和sync.Once的愛恨情仇
今天,我們將繼續(xù)探討Go語言中的兩個(gè)重要的同步工具:sync.WaitGroup 和 sync.Once。
sync.WaitGroup
sync.WaitGroup 是Go語言中的一種計(jì)數(shù)信號(hào)量,用于等待一組 goroutine 完成。它常用于等待一組并發(fā)任務(wù)全部完成后再繼續(xù)執(zhí)行。
使用方法
- 聲明一個(gè) sync.WaitGroup 類型的變量。
- 在每個(gè) goroutine 啟動(dòng)之前調(diào)用 Add 方法,增加等待計(jì)數(shù)。
- 在每個(gè) goroutine 完成時(shí)調(diào)用 Done 方法,減少等待計(jì)數(shù)。
- 在主 goroutine 中調(diào)用 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")
}
在這個(gè)例子中,main 函數(shù)啟動(dòng)了5個(gè) goroutine,每個(gè) goroutine 都會(huì)運(yùn)行 worker 函數(shù)。每個(gè) worker 在完成時(shí)調(diào)用 wg.Done(),而 main 函數(shù)會(huì)等待所有 worker 完成后再繼續(xù)執(zhí)行。
注意事項(xiàng)
- WaitGroup 的計(jì)數(shù)器不能設(shè)為負(fù)數(shù),否則會(huì)引發(fā) panic。
- 必須確保在所有 Done 調(diào)用之前已經(jīng)調(diào)用了 Add。
sync.Once
sync.Once 是一個(gè)用于確保某些操作只執(zhí)行一次的結(jié)構(gòu)體。它提供了一種線程安全的方式來執(zhí)行一次性初始化操作。
使用方法
- 聲明一個(gè) sync.Once 類型的變量。
- 使用 Do 方法執(zhí)行需要僅執(zhí)行一次的操作。
示例代碼
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()
}
在這個(gè)例子中,initialize 函數(shù)只會(huì)被執(zhí)行一次,盡管有多個(gè) goroutine 嘗試調(diào)用 once.Do(initialize)。
注意事項(xiàng)
- sync.Once 的 Do 方法接受一個(gè)無參函數(shù)。
- 即使 Do 方法被多次調(diào)用,傳入的函數(shù)也只會(huì)執(zhí)行一次。
結(jié)合使用示例
我們可以結(jié)合 sync.WaitGroup 和 sync.Once,來完成一個(gè)更復(fù)雜的并發(fā)任務(wù)。假設(shè)我們有一個(gè)初始化操作,只需執(zhí)行一次,但在多個(gè) goroutine 中執(zhí)行其他任務(wù)。
示例代碼
package main
import (
"fmt"
"sync"
"time"
)
var (
once sync.Once
wg sync.WaitGroup
)
func initialize() {
fmt.Println("Initializing...")
time.Sleep(2 * time.Second) // 模擬初始化耗時(shí)
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")
}
在這個(gè)例子中,initialize 函數(shù)只會(huì)執(zhí)行一次,而 worker 函數(shù)會(huì)并發(fā)執(zhí)行,等待所有 worker 完成后,程序才會(huì)繼續(xù)執(zhí)行。
總結(jié)
通過本文,我們了解了Go語言中的兩個(gè)重要同步工具:sync.WaitGroup 和 sync.Once。sync.WaitGroup 用于等待一組 goroutine 完成,而 sync.Once 則確保某些操作只執(zhí)行一次。這兩個(gè)工具在實(shí)際開發(fā)中非常實(shí)用,能有效地幫助我們處理并發(fā)任務(wù)。