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















 
 
 















 
 
 
 