GO WatiGroup
🔹 WaitGroup কী?
WaitGroup হলো Go-এর sync প্যাকেজের একটা structure, যেটা ব্যবহার করে আমরা বলি—
“এই কাজগুলো শেষ না হওয়া পর্যন্ত দয়া করে অপেক্ষা করো।”
সহজ ভাষায়:
👉 একাধিক goroutine কখন শেষ হবে—এটা ট্র্যাক করার টুলই WaitGroup
🔹 WaitGroup কেন দরকার?
Go-তে goroutine চালু হলে সেগুলো background-এ চলে।
সমস্যা হলো 👇main() ফাংশন শেষ হয়ে গেলে সব goroutine মারা যায়, কাজ শেষ হোক বা না হোক।
❌ WaitGroup ছাড়া সমস্যা
go doWork()
fmt.Println("Main finished")
এখানে main শেষ হয়ে গেলে doWork() মাঝপথেই বন্ধ হয়ে যেতে পারে।
✅ সমাধান → WaitGroup
WaitGroup নিশ্চিত করে:
“সব goroutine শেষ না হওয়া পর্যন্ত main() শেষ হবে না”
🔹 WaitGroup কীভাবে কাজ করে? (Concept)
WaitGroup এর ভিতরে থাকে একটা counter:
Add(n)→ কতটা কাজ শুরু হচ্ছেDone()→ একটা কাজ শেষWait()→ কাউন্টার 0 না হওয়া পর্যন্ত অপেক্ষা
📌 যখন counter = 0 → Wait() unblock হয়
🔹 WaitGroup এর ৩টা মূল মেথড
1️⃣ Add(n)
বলছে:
👉 “n টা goroutine শুরু হবে”
wg.Add(3)
2️⃣ Done()
বলছে:
👉 “এই goroutine এর কাজ শেষ”
ভিতরে ভিতরে:
wg.Done() // counter = counter - 1
📌 সাধারণত defer wg.Done() ব্যবহার করা হয় (safe)
3️⃣ Wait()
বলছে:
👉 “সব কাজ শেষ না হওয়া পর্যন্ত অপেক্ষা করো”
wg.Wait()
🔹 Simple Example (একদম beginner-friendly)
package main
import (
"fmt"
"sync"
)
func task(id int, wg *sync.WaitGroup) {
defer wg.Done() // কাজ শেষ হলে জানাবে
fmt.Println("Task", id, "started")
fmt.Println("Task", id, "finished")
}
func main() {
var wg sync.WaitGroup
wg.Add(3) // 3টা goroutine চলবে
go task(1, &wg)
go task(2, &wg)
go task(3, &wg)
wg.Wait() // সব শেষ না হওয়া পর্যন্ত অপেক্ষা
fmt.Println("All tasks completed")
}
🔍 এখানে কী হলো?
Add(3)→ counter = 3প্রতিটা goroutine শেষে
Done()→ counter কমেcounter = 0 হলে →
Wait()ছাড়েmain()শেষ হয়
🔹 কেন wg *sync.WaitGroup pointer হিসেবে পাঠানো হয়?
কারণ:
WaitGroup এর counter সব goroutine একসাথে share করে
value হিসেবে পাঠালে আলাদা copy তৈরি হবে ❌
📌 তাই সবসময়:
func task(wg *sync.WaitGroup)
🔹 Real-Life Example (সহজ ভাষায়)
ধরো 👇
তুমি ৩ জনকে কাজ দিলে:
একজন বাজার করবে
একজন রান্না করবে
একজন টেবিল সাজাবে
তুমি বললে:
“সব কাজ শেষ না হওয়া পর্যন্ত আমি খেতে বসবো না”
👉 এই “অপেক্ষা” করাটাই WaitGroup
🔹 WaitGroup কখন ব্যবহার করা হয়?
✅ যখন:
একাধিক API call
একাধিক file read/write
parallel calculation
multiple goroutine শেষ হওয়া পর্যন্ত অপেক্ষা করতে হবে
❌ যখন দরকার নেই:
শুধু data pass করতে (সেক্ষেত্রে
channel)goroutine এর মধ্যে communication (channel ভালো)
🔹 Common Mistakes ⚠️
❌ Add() goroutine এর ভিতরে
go func() {
wg.Add(1) // ভুল
}()
✔️ সবসময় goroutine চালুর আগেই Add()
❌ Done() না কল করা
➡️ program forever আটকে যাবে
❌ Negative counter
wg.Done() // Add ছাড়া
➡️ panic হবে
🔹 WaitGroup বনাম Channel (ছোট তুলনা)
| বিষয় | WaitGroup | Channel |
|---|---|---|
| কাজ শেষ হওয়া ট্র্যাক | ✅ | ❌ |
| ডাটা পাঠানো | ❌ | ✅ |
| Synchronization | ✅ | ✅ |
🔹 Summary (মনে রাখার জন্য)
WaitGroup= goroutine শেষ হওয়া পর্যন্ত অপেক্ষাAdd()→ কত কাজDone()→ কাজ শেষWait()→ অপেক্ষাmain goroutine কে alive রাখে
🔥 WaitGroup + Channel (Real API Example)
🎯 Goal (আমরা কী করবো)
একাধিক API call করবো (parallel ভাবে)
প্রতিটা API থেকে data আনবো
Channel দিয়ে data collect করবো
WaitGroup দিয়ে নিশ্চিত করবো—সব goroutine শেষ হয়েছে
🔹 Scenario (বাস্তব উদাহরণ)
ধরো:
তোমার কাছে কিছু ID আছে:
[1, 2, 3, 4]প্রতিটা ID দিয়ে API call করতে হবে:
https://jsonplaceholder.typicode.com/todos/{id}সব result এক জায়গায় collect করতে হবে
🔹 প্রথমে struct বানাই (API response অনুযায়ী)
type Todo struct {
UserID int `json:"userId"`
ID int `json:"id"`
Title string `json:"title"`
Completed bool `json:"completed"`
}
📌 এটা JSON → Go struct mapping
🔹 Worker function (goroutine যেটা API call করবে)
func getTodo(id int, wg *sync.WaitGroup, ch chan Todo) {
defer wg.Done() // কাজ শেষ জানানো
url := fmt.Sprintf("https://jsonplaceholder.typicode.com/todos/%d", id)
resp, err := http.Get(url)
if err != nil {
fmt.Println("Error:", err)
return
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
var todo Todo
json.Unmarshal(body, &todo)
ch <- todo // data পাঠানো channel এ
}
🔍 এখানে কী হচ্ছে?
goroutine শুরু
API call
JSON parse
result → channel এ পাঠানো
🔹 main() function (সবকিছু control করবে)
func main() {
ids := []int{1, 2, 3, 4}
var wg sync.WaitGroup
ch := make(chan Todo)
// goroutine চালু
for _, id := range ids {
wg.Add(1)
go getTodo(id, &wg, ch)
}
// channel বন্ধ করার logic
go func() {
wg.Wait() // সব goroutine শেষ হওয়া পর্যন্ত অপেক্ষা
close(ch) // তারপর channel বন্ধ
}()
// channel থেকে data পড়া
for todo := range ch {
fmt.Println("ID:", todo.ID, "| Title:", todo.Title)
}
}
🔹 এখানে WaitGroup + Channel একসাথে কেন দরকার?
✅ WaitGroup এর কাজ
সব goroutine কখন শেষ হবে সেটা track করা
জানে কখন
close(ch)করতে হবে
✅ Channel এর কাজ
goroutine → main goroutine এ data পাঠানো
📌 WaitGroup = synchronization
📌 Channel = communication
🔹 কেন close(ch) দরকার?
for range ch তখনই থামে যখন:
channel বন্ধ হয়
আর কোনো data নাই
👉 তাই:
wg.Wait()
close(ch)
এটা খুব গুরুত্বপূর্ণ
🔹 Real-life analogy 🧠
ধরো:
৪ জন ডেলিভারি বয় (goroutine)
সবাই আলাদা জায়গা থেকে পার্সেল আনছে
তুমি বলছো:
“সবাই আসা শেষ করলে দরজা বন্ধ করবো” →
WaitGroup“যে আসবে সে পার্সেল টেবিলে রাখবে” →
Channel
🔹 Common Mistake ⚠️
❌ main থেকে channel close করা
close(ch) // ভুল
👉 goroutine তখনও লিখছে → panic
✔️ সবসময়:
wg.Wait()
close(ch)
🔹 Diagram (মনে রাখার জন্য)
goroutine 1 ─┐
goroutine 2 ─┼──> channel ──> main
goroutine 3 ─┘
WaitGroup ──> জানে কখন সব শেষ
🔹 Interview-ready summary 🎯
WaitGroup goroutine lifecycle track করে
Channel data transfer করে
WaitGroup + Channel = safe concurrency
Add()goroutine আগেDone()defer এWait()দিয়ে close control