WaitGroups
#
// Goroutine အများကြီး ပြီးဆုံးတာကို စောင့်ဆိုင်းဖို့
// *wait group* ကို သုံးနိုင်ပါတယ်။
package main
import (
"fmt"
"sync"
"time"
)
// ဒါက goroutine တိုင်းမှာ run မယ့် function ပါ။
func worker(id int) {
fmt.Printf("Worker %d starting\n", id)
// expensive ဖြစ်တဲ့ အလုပ်တစ်ခုလိုမျိုးဖြစ်ဖို့ simulate လုပ်ပီး slepp လုပ်ခိုင်းထားပါတယ်။
time.Sleep(time.Second)
fmt.Printf("Worker %d done\n", id)
}
func main() {
// ဒီ WaitGroup ကို ဒီမှာ စတင်လိုက်တဲ့ goroutine အားလုံး
// ပြီးဆုံးတာကို စောင့်ဆိုင်းဖို့ သုံးပါတယ်။ မှတ်ချက်: WaitGroup ကို
// function တွေဆီ တိုက်ရိုက်ပေးပို့မယ်ဆိုရင် *pointer နဲ့* ပို့သင့်ပါတယ်။
var wg sync.WaitGroup
// Goroutine အများကြီးကို စတင်ပြီး WaitGroup ရဲ့
// counter ကို တစ်ခုချင်းစီ တိုးလိုက်ပါမယ်။
for i := 1; i <= 5; i++ {
wg.Add(1)
// Goroutine closure တိုင်းမှာ တူညီတဲ့ `i` တန်ဖိုးကို ပြန်သုံးတာကို ရှောင်ပါ။
// ပိုမိုအသေးစိတ်သိချင်ရင် [FAQ](https://golang.org/doc/faq#closures_and_goroutines) ကို ကြည့်ပါ။
i := i
// Worker ခေါ်တာကို closure တစ်ခုနဲ့ wrap ထားဖြင့် ဒီ closure က
// ဒီ worker ပြီးဆုံးကြောင်းကို WaitGroup ကို အသိပေးပါတယ်။ ဒီနည်းနဲ့
// worker ကိုယ်တိုင်က သူ့ကို run တဲ့ concurrency primitive တွေအကြောင်း
// သိစရာမလိုတော့ပါဘူး။
go func() {
defer wg.Done()
worker(i)
}()
}
// WaitGroup counter က 0 ပြန်ဖြစ်တဲ့အထိ block လုပ်ထားပါတယ်။
// Worker အားလုံးက သူတို့ပြီးဆုံးကြောင်း အသိပေးတဲ့အထိစောင့်မှာပါ။
wg.Wait()
// မှတ်ချက်: ဒီနည်းလမ်းက worker တွေကနေ error တွေကို ပြန်ပို့ဖို့
// straightforward မဖြစ်ပါဘူး။ ပိုပြီးအဆင့်မြင့်တဲ့ use case တွေအတွက်ဆိုရင်
// [errgroup package](https://pkg.go.dev/golang.org/x/sync/errgroup) ကို
// consider လုပ်ပါ။
}
$ go run waitgroups.go
Worker 5 starting
Worker 3 starting
Worker 4 starting
Worker 1 starting
Worker 2 starting
Worker 4 done
Worker 1 done
Worker 2 done
Worker 5 done
Worker 3 done
# Worker တွေ စတင်တဲ့အစီအစဉ်နဲ့ ပြီးဆုံးတဲ့အစီအစဉ်က
# ပရိုဂရမ်ကို run တိုင်း ကွာခြားနိုင်ပါတယ်။