Header Ads

Header ADS

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:

  1. Add(n) → কতটা কাজ শুরু হচ্ছে

  2. Done() → একটা কাজ শেষ

  3. 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")
}

🔍 এখানে কী হলো?

  1. Add(3) → counter = 3

  2. প্রতিটা goroutine শেষে Done() → counter কমে

  3. counter = 0 হলে → Wait() ছাড়ে

  4. main() শেষ হয়


🔹 কেন wg *sync.WaitGroup pointer হিসেবে পাঠানো হয়?

কারণ:

  1. WaitGroup এর counter সব goroutine একসাথে share করে

  2. value হিসেবে পাঠালে আলাদা copy তৈরি হবে ❌

📌 তাই সবসময়:

func task(wg *sync.WaitGroup)

🔹 Real-Life Example (সহজ ভাষায়)

ধরো 👇
তুমি ৩ জনকে কাজ দিলে:

  1. একজন বাজার করবে

  2. একজন রান্না করবে

  3. একজন টেবিল সাজাবে

তুমি বললে:

“সব কাজ শেষ না হওয়া পর্যন্ত আমি খেতে বসবো না”

👉 এই “অপেক্ষা” করাটাই WaitGroup


🔹 WaitGroup কখন ব্যবহার করা হয়?

✅ যখন:

  1. একাধিক API call

  2. একাধিক file read/write

  3. parallel calculation

  4. multiple goroutine শেষ হওয়া পর্যন্ত অপেক্ষা করতে হবে

❌ যখন দরকার নেই:

  1. শুধু data pass করতে (সেক্ষেত্রে channel)

  2. 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 (ছোট তুলনা)

বিষয়WaitGroupChannel
কাজ শেষ হওয়া ট্র্যাক
ডাটা পাঠানো
Synchronization

🔹 Summary (মনে রাখার জন্য)

  1. WaitGroup = goroutine শেষ হওয়া পর্যন্ত অপেক্ষা

  2. Add() → কত কাজ

  3. Done() → কাজ শেষ

  4. Wait() → অপেক্ষা

  5. main goroutine কে alive রাখে

     

     


    🔥 WaitGroup + Channel (Real API Example)

    🎯 Goal (আমরা কী করবো)

  6. একাধিক API call করবো (parallel ভাবে)

  7. প্রতিটা API থেকে data আনবো

  8. Channel দিয়ে data collect করবো

  9. WaitGroup দিয়ে নিশ্চিত করবো—সব goroutine শেষ হয়েছে


🔹 Scenario (বাস্তব উদাহরণ)

ধরো:

  1. তোমার কাছে কিছু ID আছে: [1, 2, 3, 4]

  2. প্রতিটা ID দিয়ে API call করতে হবে:

    https://jsonplaceholder.typicode.com/todos/{id}
    
  3. সব 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 এ
}

🔍 এখানে কী হচ্ছে?

  1. goroutine শুরু

  2. API call

  3. JSON parse

  4. 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 এর কাজ

  1. সব goroutine কখন শেষ হবে সেটা track করা

  2. জানে কখন close(ch) করতে হবে

✅ Channel এর কাজ

  1. goroutine → main goroutine এ data পাঠানো

📌 WaitGroup = synchronization
📌 Channel = communication


🔹 কেন close(ch) দরকার?

for range ch তখনই থামে যখন:

  1. channel বন্ধ হয়

  2. আর কোনো data নাই

👉 তাই:

wg.Wait()
close(ch)

এটা খুব গুরুত্বপূর্ণ


🔹 Real-life analogy 🧠

ধরো:

  1. ৪ জন ডেলিভারি বয় (goroutine)

  2. সবাই আলাদা জায়গা থেকে পার্সেল আনছে

  3. তুমি বলছো:

    1. “সবাই আসা শেষ করলে দরজা বন্ধ করবো” → WaitGroup

    2. “যে আসবে সে পার্সেল টেবিলে রাখবে” → 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 🎯

  1. WaitGroup goroutine lifecycle track করে

  2. Channel data transfer করে

  3. WaitGroup + Channel = safe concurrency

  4. Add() goroutine আগে

  5. Done() defer এ

  6. Wait() দিয়ে close control

  1.  

     

Powered by Blogger.