Go-তে Worker Pools এবং Goroutine Synchronization
Go প্রোগ্রামিং ভাষায় worker pool কনসেপ্টটি খুবই জনপ্রিয় এবং এটি concurrent task execution বা সমান্তরাল কাজ পরিচালনার একটি শক্তিশালী পদ্ধতি। যখন আপনার কাজের অনেকগুলি ইউনিট থাকে এবং আপনি একই সময় একাধিক কাজ চালাতে চান, তখন worker pool ব্যবহৃত হয়।
এছাড়া, goroutine synchronization হল একাধিক goroutine এর মধ্যে সমন্বয় এবং নিরাপত্তা বজায় রাখতে ব্যবহৃত কৌশল। Go তে goroutines এবং তাদের মধ্যে তথ্য ভাগাভাগি করার সময় sync প্যাকেজ ব্যবহার করে synchronization পরিচালনা করা হয়।
এই টিউটোরিয়ালে আমরা দেখব কিভাবে Go তে worker pools তৈরি করা হয় এবং goroutines এর মধ্যে synchronization কীভাবে কার্যকরভাবে করা যায়।
১. Worker Pools কী?
Worker pool হল একটি ডেভেলপমেন্ট প্যাটার্ন যেখানে অনেকগুলি কাজ (tasks) প্রসেস করার জন্য একটি নির্দিষ্ট সংখ্যা worker goroutine ব্যবহার করা হয়। এটি সাধারণত CPU-ভিত্তিক বা I/O-ভিত্তিক কাজের জন্য ব্যবহার করা হয়, যেখানে অনেকগুলি কাজ সম্পাদন করতে হবে, কিন্তু আপনি একটি নির্দিষ্ট সংখ্যা গোরাউটিন চালাতে চান।
২. Worker Pool এর মাধ্যমে Task Processing
২.১ Basic Worker Pool উদাহরণ
এখানে আমরা একটি সিম্পল worker pool তৈরি করব, যেখানে ৪টি worker goroutine থাকবে এবং সেগুলি কাজ করবে।
package main
import (
"fmt"
"time"
)
func worker(id int, jobs <-chan int, results chan<- string) {
for job := range jobs {
fmt.Printf("Worker %d started job %d\n", id, job)
time.Sleep(time.Second) // প্রতিটি কাজের জন্য ১ সেকেন্ড অপেক্ষা
results <- fmt.Sprintf("Worker %d completed job %d", id, job)
}
}
func main() {
jobs := make(chan int, 10)
results := make(chan string, 10)
// ৩টি worker goroutine তৈরি করা
for w := 1; w <= 3; w++ {
go worker(w, jobs, results)
}
// কাজগুলো worker goroutines এ পাঠানো
for j := 1; j <= 5; j++ {
jobs <- j
}
close(jobs) // কাজগুলো পাঠানোর পর jobs channel বন্ধ করে দেয়া
// ফলাফল সংগ্রহ করা
for a := 1; a <= 5; a++ {
fmt.Println(<-results)
}
}এখানে:
workerফাংশন: এটি কাজ গ্রহণ করে এবং সেই কাজটি সম্পাদন করার পর ফলাফল পাঠায়।jobsচ্যানেল: এটি worker goroutine-এ কাজ পাঠাতে ব্যবহৃত হয়।resultsচ্যানেল: worker goroutine থেকে ফলাফল গ্রহণ করে।
আউটপুট:
Worker 1 started job 1
Worker 2 started job 2
Worker 3 started job 3
Worker 1 completed job 1
Worker 2 completed job 2
Worker 3 completed job 3
Worker 1 started job 4
Worker 2 started job 5
Worker 1 completed job 4
Worker 2 completed job 5এখানে, ৩টি worker goroutines একসাথে কাজ করছে এবং ৫টি কাজ সম্পন্ন করছে। বিভিন্ন worker goroutines এ কাজগুলো ভাগ করা হয়েছে।
৩. Goroutine Synchronization
Goroutine synchronization হল একাধিক goroutine এর মধ্যে সমন্বয় করার প্রক্রিয়া, যাতে তারা একে অপরের সাথে ঠিকভাবে কাজ করতে পারে এবং কোন race condition না ঘটে।
Go তে synchronization করার জন্য sync প্যাকেজের WaitGroup, Mutex, RWMutex ইত্যাদি ব্যবহার করা হয়।
৩.১ WaitGroup এর ব্যবহার
WaitGroup ব্যবহার করে আপনি goroutines শেষ হওয়া পর্যন্ত অপেক্ষা করতে পারেন।
package main
import (
"fmt"
"sync"
"time"
)
func worker(id int, wg *sync.WaitGroup) {
defer wg.Done() // কাজ শেষ হলে ওয়েটগ্রুপে Done সিগন্যাল পাঠাবে
fmt.Printf("Worker %d is working\n", id)
time.Sleep(time.Second)
fmt.Printf("Worker %d finished\n", id)
}
func main() {
var wg sync.WaitGroup
// ৫টি worker goroutine তৈরি করা
for i := 1; i <= 5; i++ {
wg.Add(1) // ওয়েটগ্রুপে একটি কাজ যোগ করা
go worker(i, &wg)
}
// সমস্ত goroutines শেষ না হওয়া পর্যন্ত অপেক্ষা করা
wg.Wait()
fmt.Println("All workers finished")
}এখানে:
wg.Add(1): প্রতিটি নতুন goroutine জন্য একটি টাস্ক যোগ করা হয়।wg.Done(): goroutine কাজ শেষ করার পরDone()কল করে ওয়েটগ্রুপে সিগন্যাল পাঠানো হয়।wg.Wait(): এই ফাংশনটি সমস্ত goroutine শেষ হওয়া পর্যন্ত অপেক্ষা করে।
আউটপুট:
Worker 1 is working
Worker 2 is working
Worker 3 is working
Worker 4 is working
Worker 5 is working
Worker 1 finished
Worker 2 finished
Worker 3 finished
Worker 4 finished
Worker 5 finished
All workers finished৩.২ Mutex (Mutual Exclusion)
Mutex ব্যবহার করে আপনি একাধিক goroutine এর মধ্যে এক্সক্লুসিভ (exclusive) অ্যাক্সেস নিশ্চিত করতে পারেন। যখন এক goroutine কোনো রিসোর্স ব্যবহার করছে, তখন অন্য কোনো goroutine সেই রিসোর্স ব্যবহার করতে পারবে না।
package main
import (
"fmt"
"sync"
)
var counter int
var mu sync.Mutex
func increment(wg *sync.WaitGroup) {
defer wg.Done()
mu.Lock() // এক্সক্লুসিভ অ্যাক্সেস পেতে Lock করা
counter++
mu.Unlock() // কাজ শেষ হলে Unlock করা
}
func main() {
var wg sync.WaitGroup
// ১০টি goroutine তৈরি করা
for i := 1; i <= 10; i++ {
wg.Add(1)
go increment(&wg)
}
wg.Wait()
fmt.Println("Counter:", counter) // আউটপুট: Counter: 10
}এখানে:
mu.Lock(): একটি goroutine যখন রিসোর্স ব্যবহার করতে চায় তখনLock()কল করে তা ব্লক করে।mu.Unlock(): কাজ শেষ হলেUnlock()কল করে রিসোর্স মুক্ত করা হয়।
আউটপুট:
Counter: 10এখানে, প্রতিটি goroutine একটি নির্দিষ্ট সংখ্যক ইনক্রিমেন্ট করার পর mutex ব্যবহার করে সিঙ্ক্রোনাইজ করা হয়েছে, যাতে race condition এড়ানো যায়।
৪. RWMutex (Read-Write Mutex)
RWMutex ব্যবহার করে আপনি পড়া (read) এবং লেখা (write) অপারেশনগুলোর জন্য আলাদা আলাদা লক পরিচালনা করতে পারেন। এটি যখন আপনি concurrent রিড এবং এক্সক্লুসিভ রাইট অপারেশন করতে চান তখন ব্যবহার হয়।
package main
import (
"fmt"
"sync"
)
var counter int
var rwmu sync.RWMutex
func readCounter(wg *sync.WaitGroup) {
defer wg.Done()
rwmu.RLock() // রিড লক করা
fmt.Println("Counter:", counter)
rwmu.RUnlock() // রিড লক মুক্ত করা
}
func writeCounter(wg *sync.WaitGroup) {
defer wg.Done()
rwmu.Lock() // রাইট লক করা
counter++
rwmu.Unlock() // রাইট লক মুক্ত করা
}
func main() {
var wg sync.WaitGroup
// ৫টি রিড এবং ৫টি রাইট goroutine তৈরি করা
for i := 0; i < 5; i++ {
wg.Add(1)
go readCounter(&wg)
wg.Add(1)
go writeCounter(&wg)
}
wg.Wait()
fmt.Println("Final Counter:", counter)
}এখানে:
rwmu.RLock(): রিড লক ব্যবহার করা, যার মাধ্যমে অনেকগুলি goroutine একসাথে রিড করতে পারে।rwmu.Lock(): এক্সক্লুসিভ রাইট লক ব্যবহার করা, যাতে শুধুমাত্র এক goroutine রাইট করতে পারে।
সারসংক্ষেপ
- Worker Pools: Go-তে worker pool কনসেপ্ট ব্যবহার করে একাধিক goroutine তৈরি করা হয় যা কাজগুলি সমান্তরালে সম্পাদন করে।
- Goroutine Synchronization: গোরাউটিনগুলির মধ্যে সমন্বয় করার জন্য
syncপ্যাকেজেরWaitGroup,Mutex, এবংRWMutexব্যবহার
করা হয়।
- WaitGroup: একাধিক goroutine এর সমাপ্তি পর্যন্ত অপেক্ষা করার জন্য ব্যবহৃত হয়।
- Mutex: একটি রিসোর্সের এক্সক্লুসিভ অ্যাক্সেস নিশ্চিত করতে ব্যবহৃত হয়।
- RWMutex: রিড-ওয়াইট লক ব্যবহারের মাধ্যমে একাধিক রিড অপারেশন এবং এক্সক্লুসিভ রাইট অপারেশন পরিচালনা করা হয়।
Go তে worker pool এবং goroutine synchronization ব্যবহার করে আপনি concurrent কাজগুলো সহজেই পরিচালনা করতে পারবেন, যা আপনার প্রোগ্রামকে আরও কার্যকর এবং কার্যকরী করে তোলে।
Read more