Worker Pools এবং Goroutine Synchronization

Advanced Concurrency Patterns (অ্যাডভান্সড কনকারেন্সি প্যাটার্নস) - গো প্রোগ্রামিং (Go Programming) - Computer Programming

283

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 কাজগুলো সহজেই পরিচালনা করতে পারবেন, যা আপনার প্রোগ্রামকে আরও কার্যকর এবং কার্যকরী করে তোলে।

Content added By
Promotion

Are you sure to start over?

Loading...