Goroutine 是 Go 語言中一種輕量級的並發執行單元。我們可以使用 Goroutine 來實現並發編程,提高程式的效率和性能。
sync.WaitGroup 是 Go 標準庫中提供的一個同步原語,它用於等待一組 Goroutine 全部執行完畢。我們可以通過以下方式使用 sync.WaitGroup:
創建 WaitGroup 實例:
wg := new(sync.WaitGroup)
使用 wg.Add(n) 可以將 WaitGroup 的計數器增加 n。每啟動一個新的 Goroutine,都需要增加計數器。
增加 WaitGroup 計數器:
wg.Add(n)
等待所有 Goroutine 完成
wg.Wait()
在 main 函數中,使用 wg.Wait() 可以阻塞直到所有 Goroutine 都完成。
在 Goroutine 中減少計數器:
defer wg.Done()
在 Goroutine 內部,使用 defer wg.Done() 可以在 Goroutine 退出時,將 WaitGroup 的計數器減 1。
下方為實作的code,找質數:一般如果用for迴圈會非常慢
package main
import (
"fmt"
"time"
)
func main() {
num := 300000
start := time.Now()
for i := 1; i <= num; i++ {
if isPrime(i) {
fmt.Println(i)
}
}
end := time.Now()
fmt.Println(end.Unix() - start.Unix(), "seconds")
}
func isPrime(num int) bool {
if num == 1 {
return false
} else if num == 2 {
return true
} else {
for i := 2; i < num; i++ {
if num % i == 0 {
return false
}
}
return true
}
}
單純只使用Goroutine,在使用findPrimes之前加go,會沒辦法知道實際到底花幾秒的時間,以及main時間到就會結束。
main 啟動了 300000 個 Goroutine,每個 Goroutine 調用 findPrimes 函數。
但是 main 函數並沒有等待所有 Goroutine 執行完畢,而是直接等待了 2 秒鐘輸出 done。
單純使用 Goroutine 而不使用 sync.WaitGroup 或其他同步機制,無法確保所有任務都已完成,也無法準確測量總的執行時間。
package main
import (
"fmt"
"time"
)
func main() {
num := 300000
for i := 1; i <= num; i++ {
go findPrimes(i)
}
time.Sleep(2 * time.Second)
fmt.Println("done")
}
func findPrimes(num int) {
if num == 1 {
return
} else if num == 2 {
fmt.Println(num)
} else {
for i := 2; i < num; i++ {
if num % i == 0 {
return
}
}
fmt.Println(num)
}
}
因此,我們無法確定在 “done” 輸出之前,所有 findPrimes 函數都已經執行完畢,要解決這個問題,最簡單的方法就是使用 sync.WaitGroup。
下方正確使用了 sync.WaitGroup 來確保所有 Goroutine 都執行完畢,並且能夠正確地計算整個過程的耗時。
package main
import (
"fmt"
"sync"
"time"
)
func main() {
wg := new(sync.WaitGroup)
num := 300000
wg.Add(num) // 設置需要檢查的數字範圍為 1 到 300000
start := time.Now().Unix()
for i := 1; i <= num; i++ {
go findPrimes(i, wg) // 啟動 num 個 goroutine,每個 goroutine 都調用 findPrimes 函數。
}
wg.Wait() // 在所有goroutine完成之前, main 函數會一直等待,使用 wg.Wait()
end := time.Now().Unix()
fmt.Print(end-start, "seconds")
}
func findPrimes(num int, wg *sync.WaitGroup) {
defer wg.Done() // 確保在函數退出時,WaitGroup 的計數器會被減 1
if num == 1 {
return
} else if num == 2 {
fmt.Println(num)
} else {
for i := 2; i < num; i++ {
if num%i == 0 {
return
}
}
fmt.Println(num)
}
}