在 DAY 2~DAY 4 我們使用到了 goroutine、lock、channel,主要目標在保護併發下的資料避免 race condition,接下來會以提高執行效率為討論重點。
什麼是 Thread-Per-Message Pattern?
每一個 message 開啟一個 goroutine 來運行
如果有多個 task 需要運行,每個 task 的執行時間過長,比如 IO 或者是複雜的運算,那一個一個執行 task 是相當浪費時間的,只要 task 沒有執行順序的需求,可以將所有 task 都同時執行。
並且 Thread-Per-Message Pattern 是用在不需要 goroutine 回傳值的情境中,如果需要回傳值,可以使用 Future Pattern,接下來的文章會再介紹。
問題情境
設計一個推播新聞系統,會將新的新聞直接推播出去,我們希望推播系統效率要高。
相關的 code 在Github - go-design-patterns。
實作有問題的系統如下:
package main
import (
"fmt"
"time"
)
func PushNews(news string, startTime time.Time) {
time.Sleep(time.Duration(3 * time.Second)) //模擬推播運行的時間
fmt.Printf("%s cost %s\n", news, time.Since(startTime))
}
func main() {
start := time.Now()
allNews := []string{
"中秋節來了",
"記得",
"不要戶外烤肉~",
}
for _, news := range allNews {
PushNews(news, start)
}
fmt.Printf("cost %s", time.Since(start))
}
由於每則新聞都是一則一則推播,所以耗時了約 9 秒
解決方式
在PushNews()
加上go
就可以啟動 goroutine 來推播
package main
import (
"fmt"
"time"
)
func PushNews(news string, startTime time.Time) {
time.Sleep(time.Duration(3 * time.Second)) //模擬推播運行的時間
fmt.Printf("%s cost %s\n", news, time.Since(startTime))
}
func main() {
start := time.Now()
allNews := []string{
"中秋節來了",
"記得",
"不要戶外烤肉~",
}
for _, news := range allNews {
go PushNews(news, start)
}
time.Sleep(10 * time.Second) //等待goroutine執行完畢
}
由於是同時推播,所以推播只花了 3 秒