1. سماجیکرداری کے آلات کا کردار

متقابل کارروائی پروگرامنگ میں، جب کئی گوروٹینیز مشترکہ وسائل استعمال کرتے ہیں، تو یہ ضروری ہے کہ یہ یقینی بنایا جائے کہ وسائل صرف ایک گوروٹینیز کی مدد سے ہی ایک وقت میں استعمال ہو سکتے ہیں تاکہ ریس کی حالت سے بچا جا سکے۔ اس کے لئے سماجیکرداری کے آلات کا استعمال ضروری ہوتا ہے۔ سماجیکرداری کے آلات مختلف گوروٹینیز کے مشترکہ وسائل تک رسائی کو منظم کر سکتے ہیں، جو متقابل ماحول میں ڈیٹا کی یکسانیت اور حالت کی سماجیکرداری کو یقینی بناتے ہیں۔

Go زبان میں سماجیکرداری کے آلات کی ایک وسیع سلسلہ فراہم کیا گیا ہے، جس میں یہ شامل ہے لیکن اس سے محدود نہیں ہیں:

  • میوٹیکس (sync.Mutex) اور پڑھ-لکھ میوٹیکس (sync.RWMutex)
  • چینلز
  • انتظار گروپس
  • ایٹومک فنکشنز (ایٹومک پیکیج)
  • شرائطی متغیرات (sync.Cond)

2. سماجیکرداری کے ابتدائی مقامات

2.1 میوٹیکس (sync.Mutex)

2.1.1 میوٹیکس کا تصور اور کردار

میوٹیکس ایک سماجیکرداری آلہ ہے جو مشترکہ وسائل کی محفوظ عمل کی یقینی بناتا ہے، اس کے ذریعے صرف ایک گوروٹینیز کو ایک وقت میں لاک ڈالنے کی اجازت دیتا ہے تکہ وقت کسی دیئے گئے وقت میں باندھا جا سکے۔ میوٹیکس Lock اور Unlock میتھڈز کے ذریعے سماجیکرداری کو حاصل کرتا ہے۔ Lock میتھڈ کو بلوک ہو جائے گا جب تک لاک ریلیز نہیں ہوتا، اور اس وقت دوسرے گوروٹینیز جو کہ لاک حاصل کرنے کی کوشش کر رہے ہوں انتظار کریں گے۔ Unlock کونارسا کرتا ہے، دوسرے انتظار کر رہے گوروٹینیز کو لاک کرنے کی اجازت دیتا ہے۔

var mu sync.Mutex

func criticalSection() {
    // یقینی بنانے کے لئے لاک حاصل کریں کہ منحصر وسائل تک رسائی
    mu.Lock()
    // یہاں مشترکہ وسائل تک رسائی کریں
    // ...
    // لاک کو منسلک کر کے دوسرے گوروٹینیز کو اسے حاصل کرنے کی اجازت دیں
    mu.Unlock()
}

2.1.2 میوٹیکس کی عملی استعمال

مان لیں ہمیں ایک گلوبل کاؤنٹر کو برقرار رکھنا ہے، اور متعدد گوروٹینیز کو اس کی قیمت بڑھانے کی ضرورت ہے۔ میوٹیکس استعمال کر کے ہم کاؤنٹر کی درستگی کو یقینی بنا سکتے ہیں۔

var (
    mu      sync.Mutex
    counter int
)

func increment() {
    mu.Lock()         // تبدیلی کرنے سے پہلے لاک
    counter++         // کاؤنٹر کو بہتری کے ساتھ بڑھائیں
    mu.Unlock()       // عمل کے بعد لاک کھولیں، دوسرے گوروٹینیز کو کاؤنٹر تک رسائی فراہم کرنے دیں
}

func main() {
    for i := 0; i < 10; i++ {
        go increment()  // کاؤنٹر کی قیمت بڑھانے کے لئے متعدد گوروٹینیز شروع کریں
    }
    // کچھ وقت (عمل میں، آپ کو ویٹ گروپ یا دوسرے طریقوں سے تمام گوروٹینیز کے مکمل ہونے کے لئے انتظار کرنا چاہئے)
    time.Sleep(1 * time.Second)
    fmt.Println(counter)  // کاؤنٹر کی قیمت کا انتظام کریں
}

2.2 پڑھ-لکھ میوٹیکس (sync.RWMutex)

2.2.1 پڑھ-لکھ میوٹیکس کا تصور

RWMutex ایک خاص قسم کا لاک ہے جو اجازت دیتا ہے کہ متعدد گوروٹینیز میں سے کئی وقت مشترکہ وسائل کو ایک وقت میں پڑھیں جبکہ لکھائی کے عمل محفوظ ہوں۔ میوٹیکس کے مقابل میں، پڑھ-لکھ لاک متعدد پڑھنے والے سیٹواتش میں کارکردگی میں اضافہ کر سکتا ہے۔ اس کے چار میتھڈز ہیں: 'RLock'، 'RUnlock' پڑھنے والے عمل کو لاک کرنے اور کھولنے کے لئے اور 'Lock'، 'Unlock' لکھانے والے عمل کے لئے۔

2.2.2 پڑھ-لکھ میوٹیکس کی عملی استعمال کی صورت میں

ایک ڈیٹا بیس اے ایپلیکیشن میں، پڑھنے والے عملات کبھی-کباڑ عملات سے کئی زیادہ ہوسکتے ہیں۔ پڑھ-لکھ لاک کا استعمال سسٹم کی کارکردگی میں بہتری کرتا ہے کیونکہ یہ متعدد گوروٹینیز کو ایک وقت میں پڑھنے کی اجازت دیتا ہے۔

var (
    rwMu  sync.RWMutex
    data  int
)

func readData() int {
    rwMu.RLock()         // پڑھنے کا لاک حاصل کریں، جو دیگر پڑھنے والے عملات کو متزامن طریقے سے آگے بڑھنے دیتا ہے
    defer rwMu.RUnlock() // دیفر کا استعمال کرتے ہوئے یقینی بنائیں کہ لاک دیا گیا ہے
    return data          // ڈیٹا کا محفوظ پڑھیں
}

func writeData(newValue int) {
    rwMu.Lock()          // لکھنے کے وقت لاک حاصل کریں، جو اس وقت دوسرے پڑھنے یا لکھنے والے عملات کو روکتا ہے
    data = newValue      // نئی قیمت کو محفوظ کرنا
    rwMu.Unlock()        // مکمل کرنے کے بعد لکھنے کو روک دیں
}

func main() {
    go writeData(42)     // ایک گوروٹینیز شروع کرتا ہے کہ لکھنے کا عمل انجام دے
    fmt.Println(readData()) // میں گوروٹینیز نے پڑھائی عمل انجام دیا
    // یہ سنچرنیزیشن کے وقت تمام گوروٹینیز مکمل ہونے کی اطمینان کے لئے ویٹ گروپ یا دوسری سماجیکرداری کی مناسبت کا استعمال کریں
}

مثال میں، متعدد پڑھنے والے یہی readData فنکشن کو ایک وقت میںنقاب چیز کرو سکتے ہیں، لیکن ایک لکھنے والے گوروٹین انتظار لپیٹتا ہے اور دیگر گوروٹینیز کو بلاک کرتا ہے۔ یہ ترتیب منصفانہ فوائد فراہم کرتا ہے جب بھی پڑھنے سے زیادہ اور لکھنے سے کم ہے۔

2.3 شرائطی متغیرات (sync.Cond)

2.3.1 شرطی متغیرات کا تصور

Go زبان میں ہماہنگی کی میکانیزم میں، شرطی متغیرات وقت کے مطابقت میں کسی شرط کی تبدیلی کا انتظار کرنے یا مطابقت کی نوٹیفائی کے لئے استعمال ہوتے ہیں۔ شرطی متغیرات ہمیشہ ایک میوٹیکس (sync.Mutex) کے ساتھ استعمال ہوتے ہیں، جو کہ شرط کی مستقلیت کو محفوظ رکھنے کے لئے استعمال ہوتا ہے۔

شرطی متغیرات کا تصور عام طور پر ایپریٹنگ سسٹم ڈومین سے آتا ہے، جو گوروٹین کی ایک گروہ کو کسی شرط کی پوری ہونے کا انتظار کرنے کی اجازت دیتا ہے۔ خاص طور پر، ایک گوروٹین کسی شرط کی پوری ہونے کا انتظار کرتے ہوئے اپنی کارروائی معطل کر سکتا ہے، اور دوسری گوروٹین شرط کو بدل کر انتظار کر رہی گوروٹینز کو نوٹیفائی کر سکتی ہے۔

Go معیاری کتابخانے میں، شرطی متغیرات کو sync.Cond قسم کے ذریعے فراہم کیا گیا ہے، اور اس کی اہم میتھڈز میں شامل ہیں:

  • Wait: اس میتھڈ کو بلانے سے مستقل کو بلاگ کرتا ہے اور منتظر ہو جاتا ہے جب تک کوئی دوسری گوروٹین اسی شرط کی شرطی متغیر پر Signal یا Broadcast کو بلاتا نہیں، اس کے بعد وہ دوبارہ میوٹیکس حاصل کرنے کی کوشش کرے گا۔
  • Signal: یہ میتھڈ اس شرطی متغیر کے لئے منتظر گوروٹین کو جاگا دیتا ہے۔ اگر کوئی گوروٹین منتظر نہیں ہوتی تو، اس میتھڈ کو بلانے کا کوئی اثر نہیں ہوتا ہے۔
  • Broadcast: یہ تمام منتظر گوروٹینز کو جاگا دیتا ہے۔

شرطی متغیرات کو نقل نہیں کیا جانا چاہئے، اس لئے عام طور پر یہ کسی خاص سٹرکٹ کے پوائنٹر فیلڈ کے طور پر استعمال کیا جاتا ہے۔

2.3.2 شرطی متغیرات کے عملی معاملات

امدادی شرطوں کا استعمال کرتے ہوئے شرطی متغیرات کا استعمال دکھانے والا ایک مثال یہاں ہے جو ایک سادہ پیداکار-استہلک کا نمونہ دیتا ہے:

package main

import (
    "fmt"
    "sync"
    "time"
)

// SafeQueue ایک میوٹیکس سے حفاظتی قوانین سے محفوظ قطار ہے
type SafeQueue struct {
    mu    sync.Mutex
    cond  *sync.Cond
    queue []interface{}
}

// Enqueue قطار کے آخر میں ایک عنصر شامل کرتا ہے اور منتظر گوروٹینز کو مطلع کرتا ہے
func (sq *SafeQueue) Enqueue(item interface{}) {
    sq.mu.Lock()
    defer sq.mu.Unlock()

    sq.queue = append(sq.queue, item)
    sq.cond.Signal() // منتظر گوروٹین کو مطلع کرتا ہے کہ قطار خالی نہیں ہے
}

// Dequeue قطار کے شروع سے ایک عنصر ہٹاتا ہے، اگر قطار خالی ہو تو انتظار کرتا ہے
func (sq *SafeQueue) Dequeue() interface{} {
    sq.mu.Lock()
    defer sq.mu.Unlock()

    // جب قطار خالی ہو
    for len(sq.queue) == 0 {
        sq.cond.Wait() // شرط میں تبدیلی کا انتظار کرتا ہے
    }

    item := sq.queue[0]
    sq.queue = sq.queue[1:]
    return item
}

func main() {
    queue := make([]interface{}, 0)
    sq := SafeQueue{
        mu:    sync.Mutex{},
        cond:  sync.NewCond(&sync.Mutex{}),
        queue: queue,
    }

    // پیداکار گوروٹین
    go func() {
        for i := 0; i < 5; i++ {
            time.Sleep(1 * time.Second)         // تیاری کا وقت بنانا
            sq.Enqueue(fmt.Sprintf("آئٹم%d", i)) // عنصر پیدا کرنا
            fmt.Println("تیاری:", i)
        }
    }()

    // استہلک گوروٹین
    go func() {
        for i := 0; i < 5; i++ {
            item := sq.Dequeue() // ایک عنصر استہلک کرنا، انتظار کرنا اگر قطار خالی ہو
            fmt.Printf("استہلک کریں: %v\n", item)
        }
    }()

    // کافی دیر تک انتظار کریں تاکہ تمام پیداکاری اور استہلکی مکمل ہو جائے
    time.Sleep(10 * time.Second)
}

اس مثال میں، ہم نے ایک SafeQueue ڈھانچا تعریف کیا ہے جس میں ایک اندرونی قطار اور ایک شرطی متغیر ہے۔ جب کنسیومر Dequeue میتھڈ کو بلاتا ہے اور قطار خالی ہے، تو Wait میتھڈ کا استعمال کرکے انتظار کرتا ہے۔ جب پروڈیوسر Enqueue میتھڈ کو بلاتا ہے اور ایک نیا عنصر قطار میں شامل کرتا ہے، تو وہ Signal میتھڈ کا استعمال کرکے انتظار کرتے ہوئے کنسیومر کو جاگاتا ہے۔

2.4 WaitGroup

2.4.1 WaitGroup کا تصور اور استعمال

sync.WaitGroup ایک ہماہنگی میکانیزم ہے جو ایک گوروٹین کی گروہ کی مکمل ہونے کا انتظار کرنے کے لئے استعمال ہوتا ہے۔ جب آپ ایک گوروٹین شروع کرتے ہیں، تو آپ Add میتھڈ کو بلانے سے کونٹر کو بڑھا سکتے ہیں، اور ہر گوروٹین جب کام ختم ہو جاتی ہے تو Done میتھڈ (جو واقع میں Add(-1) کرتا ہے) کو بلاتی ہے۔ اصل گوروٹین مکمل ہونے کا اشارہ دینے کے لئے، اصل گوروٹین Wait میتھڈ کو بلا سکتی ہے، جب تک کونٹر صفر نہ ہو جاتا ہے، جس طرح کے تمام گوروٹینز نے اپنے کام مکمل کرلیا ہو۔

WaitGroup کا استعمال کرتے وقت مندرجہ ذیل نکتوں کا خیال رکھنا چاہئے:

  • Add، Done، اور Wait میتھڈز تھریڈ-حفاظت کے نہیں ہیں اور انہیں متعدد گوروٹینز میں متوازی طور پر بلا نہیں جانا چاہئے۔
  • Add میتھڈ کو نئے بنائی گئی گوروٹین شروع ہونے سے پہلے بلایا جانا چاہئے۔

2.4.2 ویٹ گروپ کا عملی استعمال

یہاں ویٹ گروپ کا استعمال کرنے کا ایک مثال ہے:

package main

import (
	"fmt"
	"sync"
	"time"
)

func worker(id int, wg *sync.WaitGroup) {
	defer wg.Done() // تکمیل ہونے پر ویٹ گروپ کو مطلع کریں

fmt.Printf("کارکن %d شروع ہو رہا ہے\n", id)
time.Sleep(time.Second) // وقت کی مصنوعی دیر تک عمل
fmt.Printf("کارکن %d مکمل ہو گیا\n", id)
}

func main() {
var wg sync.WaitGroup

for i := 1; i <= 5; i++ {
wg.Add(1) // گوروٹین شروع ہونے سے پہلے کونٹر بڑھاؤ
go worker(i, &wg)
}

wg.Wait() // تمام کارکن گوروٹینز کا انتظار کریں کہ وہ مکمل ہوں
fmt.Println("تمام کارکن مکمل ہو گئے ہیں")
}

اس مثال میں، کارکن کا فنکشن ایک ٹاسک کے اجراء کا نمائندگی کرتا ہے۔ مین فنکشن میں، ہم پانچ کارکن گوروٹین شروع کرتے ہیں۔ ہر گوروٹین شروع ہونے سے پہلے ہم wg.Add(1) کو کال کرتے ہیں تاکہ ویٹ گروپ کو مطلع کیا جائے کہ ایک نیا ٹاسک اجراء ہو رہا ہے۔ جب ہر کارکن فنکشن مکمل ہوتا ہے، تو وہ defer wg.Done() کو کال کرتا ہے تاکہ ویٹ گروپ کو مطلع ہو کر ٹاسک مکمل ہو گیا ہے۔ گوروٹین کو شروع کرنے کے بعد، مین فنکشن wg.Wait() پر بلاک ہو جاتا ہے جب تک تمام کارکن رپورٹ کرتے نہیں ہوجاتے۔

2.5 ایٹمک عملیات (sync/atomic)

2.5.1 ایٹمک عملیات کا تصور

ایٹمک عملیات میں سماجی پروگرامنگ کے عملوں کا تصور ہے جو ایک ہم وقت مین، یعنی ان کو دوسرے عملوں کی اجراء کے دوران کاٹا نہیں جا سکتا ہے۔ متعدد گوروٹینز کے لئے، ایٹمک عملیات کا استعمال ڈیٹا کی استمراریت اور حالت کا ہم سازی بغیر لاکنگ کے، ایٹمک عملیات خود ہم وقتی کی تصدیق کرتے ہیں۔

Go زبان میں، sync/atomic پیکیج منصوبہ کمی کے اتمامی یاداشتی عملیات فراہم کرتا ہے۔ int32، int64، uint32، uint64، uintptr، اور pointer جیسے بنیادی ڈیٹا ٹائپس کے لئے، sync/atomic پیکیج کے تراکیب کا استعمال سیکیور متعدد عملات کے لئے کیا جا سکتا ہے۔ ایٹمک عملیات کی اہمیت اسمیں ہے کہ یہ دوسری ہم وقتی اعلی معماری (جیسے تالیف اور حالت متغیرات) کا بنیاد ہیں اور عموماً لاکنگ آلات سے زیادہ کار گر ہوتے ہیں۔

2.5.2 ایٹمک عملیات کا عملی استعمال

ایک سناریو کو عملی استعمال میں لیں جہاں ہمیں ویب سائٹ کے برابر میں آنے والے بازیداروں کی متعدد تعداد کا پیچیدہ پیمانہ کرنا ہو۔ ایک سادہ کاؤنٹر متغیر کا استعمال کرتے ہوئے، ہم منطقی طور پر کاؤنٹر میں اضافہ کریں گے جب کسی بازیدار آئے گا اور اسے گھٹائیں گے جب کوئی بازیدار چلا جائے گا۔ لیکن، ایک ہم وقت ماحول میں، یہ طریقہ داد خواہی کو لے کر آرہا ہے۔اس لئے، ہم sync/atomic پیکیج کو استعمال کر سکتے ہیں تاکہ کاؤنٹر کو بے خطر اور محفوظ طریقے سے بدل سکیں۔

package main

import (
"fmt"
"sync"
"sync/atomic"
"time"
)

var visitorCount int32

func incrementVisitorCount() {
atomic.AddInt32(&visitorCount, 1)
}

func decrementVisitorCount() {
atomic.AddInt32(&visitorCount, -1)
}

func main() {
var wg sync.WaitGroup
for i := 0; i < 100; i++ {
wg.Add(1)
go func() {
incrementVisitorCount()
time.Sleep(time.Second) // بازیدار کی ملاقات کا وقت
decrementVisitorCount()
wg.Done()
}()
}
wg.Wait()
fmt.Printf("موجودہ بازیدار کی تعداد: %d\n", visitorCount)
}

اس مثال میں، ہم نے 100 گوروٹینز کو تخلیق دینے کے لئے استعمال کیا ہے تاکہ بازیداروں کی آمد و رفت کی نمائندگی کر سکیں۔ atomic.AddInt32() فنکشن کا استعمال کر کے، ہم یقینی بناتے ہیں کہ کاؤنٹر کے اضافے اور گھٹاؤ کو ہم وقتی ماحول میں بھی ایٹمک ہوتے ہیں، بہت زیادہ مشغلہ ہونے کی صورت میں بھی۔

2.6 چینل ہم وقتی میکانزم

2.6.1 چینل کے ہم وقتی خصوصیات

چینلز Go زبان میں گوروٹینز کے درمیان بات چیت کرنے کا ایک طریقہ ہیں۔ ایک چینل ڈیٹا بھیجنے اور موصول کرنے کی صلاحیت فراہم کرتا ہے۔ جب ایک گوروٹین چینل سے ڈیٹا پڑھنے کی کوشش کرتا ہے اور چینل میں ڈیٹا نہیں ہوتا تو، یہ وقت پر بلاک ہو جاتا ہے جب تک وہ دستیاب نہیں ہوجاتا۔ اسی طرح، اگر چینل بھرا ہوتا ہے (غیر بفرض چینل کے لئے، یہ مطلب ہوتا ہے کہاس میں پہلے ہی ڈیٹا ہوتا ہے) تو ڈیٹا بھیجنے والا گوروٹین بھی بلاک ہو جاتا ہے جب تک وسیع رقبہ میں لکھنے کی جگہ نہیں ہوتی ہے۔ یہ خصوصیت چینلز کو گوروٹینز کے درمیان ہم وقتی کے لئے بہت کار آمد بناتی ہے۔

2.6.2 چینلز کے ساتھ سماج کے استعمال کے مواقع

فرض کریں ہمارے پاس ایک کام ہے جسے مکمل کرنے کے لیے متعدد گوروٹینز ہیں، ہر ایک سب ٹاسک سے نمٹ رہا ہے، اور پھر ہمیں تمام سب ٹاسک کے نتائج کو ایکٹیگریٹ کرنا ہے۔ ہم ایک چینل کا استعمال کرکے تمام گوروٹینز کے مکمل ہونے کا انتظار کرنے کے لیے استعمال کرسکتے ہیں۔

package main

import (
    "fmt"
    "sync"
)

func worker(id int, wg *sync.WaitGroup, resultChan chan<- int) {
    defer wg.Done()
    // کچھ عمل انجام دینے کے لیے...
    fmt.Printf("کام کرنے والا %d شروع ہو رہا ہے\n", id)
    // فرض کریں کہ سب ٹاسک کا نتیجہ ورکر کا ID ہے
    resultChan <- id
    fmt.Printf("کام کرنے والا %d مکمل ہو گیا\n", id)
}

func main() {
    var wg sync.WaitGroup
    numWorkers := 5
    resultChan := make(chan int, numWorkers)

    for i := 0; i < numWorkers; i++ {
        wg.Add(1)
        go worker(i, &wg, resultChan)
    }

    go func() {
        wg.Wait()
        close(resultChan)
    }()

    // تمام نتائج جمع کریں
    for result := range resultChan {
        fmt.Printf("نتیجہ موصول ہوا: %d\n", result)
    }
}

اس مثال میں، ہم 5 گوروٹینز شروع کرتے ہیں تاکہ ٹاسک انجام دے سکیں اور نتائج resultChan چینل کے ذریعے جمع کریں۔ میں گرلین کا انتظار کر رہا ہے کہ کسی دوسرے گوروٹین کا کام کردینے کیلئے اور پھر نتیجہ چینل کو بند کرتا ہے۔ اسکے بعد، مین گوروٹینز resultChan چینل کو چھوتی ہے، تمام گوروٹینز کے نتائج کو جمع کرکے چھاپتا ہے۔

2.7 ایک دفعہ اجراء (sync.Once)

sync.Once ایک سماجی کی بنیادی سڑک ہے جو برامد کرتا ہے کہ ایک عمل کو صرف ایک بار پروگرام کے اجراء کے دوران اجراء کیا جائے۔ sync.Once کا ایک عام استعمال ایک سنگلٹن شیئرن کے آغاز یا تاخیری ابتدائیکرنے کی صورت میں ہوتا ہے۔ چاہے اس عمل کو کتنی ہی زیادہ گوروٹینز کال کریں، یہ صرف ایک بار چلے گا، اس لیے Do فنکشن کا نام ہے۔

sync.Once برابر تیناتوتی کی مسئلے اور اجراء کی فعالیت کی کشمار کئے بغیر کنکرنسی کے مسائل کو مکمل طور پر دور کرتا ہے۔

sync.Once کا استعمال دکھانے کے لیے ایک سادہ مثال کی شکل میں:

package main

import (
    "fmt"
    "sync"
)

var once sync.Once
var instance *Singleton

type Singleton struct{}

func Instance() *Singleton {
    once.Do(func() {
        fmt.Println("اب سنگل انسٹنس بنایا جا رہا ہے۔")
        instance = &Singleton{}
    })
    return instance
}

func main() {
    for i := 0; i < 10; i++ {
        go Instance()
    }
    fmt.Scanln() // باہر پچھتاو کا انتظار کریں
}

اس مثال میں، چاہے Instance فنکشن کو متعدد گوروٹینز نے متعدد بار کال کیا ہو، سنگلٹن کا انسٹنس صرف ایک بار بنایا جائے گا۔ دوسری کالز سیدھے طور پر پہلی دفعہ بنائے گئے سنگلٹن انسٹنس کو واپس لوٹائے گا، یہ یقینی بناتا ہے کہ انسٹنس کی یکتاؤ ہوگی۔

2.8 ار گروپ

ErrGroup Go زبان میں استعمال ہونے والی ایک لائبریری ہے جو متعدد گوروٹینز کو ہم آہنگ بنانے اور ان کی خرابیوں کو جمع کرنے کا استعمال ہوتا ہے۔ یہ "golang.org/x/sync/errgroup" پیکیج کا حصہ ہے، متعدد آپریشنز میں خرابیوں کا عمل بردار ترین طریقہ فراہم کرنے کا مختصر طریقہ ہے۔

2.8.1 ار گروپ کا تصور

ErrGroup کا بنیادی تصور ہے کہ ایک مجموعہ متعلقہ تسکس کو (عام طور پر ایک ساتھ اجراء ہوتے ہیں) آپس میں باندھا جائے، اور اگر ان میں سے کوئی بھی تسک ناکام ہو جائے تو پورے گروپ کی اجراء منسوخ کر دی جائے گی۔ اسی وقت، اگر ان میں سے کوئی متوازی آپریشن خرابی واپس کرے، تو ErrGroup یہ خرابی پکڑے گا اور اس خرابی کو واپس کرے گا۔

ErrGroup کو استعمال کرنے کے لیے، سب سے پہلے پیکیج درآمد کریں:

import "golang.org/x/sync/errgroup"

پھر، ErrGroup کا ایک نمونہ بنائیں:

var g errgroup.Group

اس کے بعد، آپ تسکس کو کلوجرس کی شکل میں ErrGroup کو مندرجہ ذیل کرسکتے ہیں، اور Go میتھڈ کو بلانے سے نئی گوروٹین شروع کرسکتے ہیں:

g.Go(func() error {
    // کچھ خاص تسک ادا کریں
    // اگر سب ٹھیک ہوگیا
    return nil
    // اگر خرابی واپس آئے
    // return fmt.Errorf("خرابی واپس آ گئی")
})

آخر کار، Wait میتھڈ کو بلایا جائے گا، جو تمام کاموں کا انتظار کر کے بند ہوگا۔ اگر ان میں سے کوئی تسک خرابی واپس کرے، تو Wait وہ خرابی واپس کریگا:

if err := g.Wait(); err != nil {
    // خرابی کا سامنا کریں
    log.Fatalf("تسک کی اجراء میں خرابی: %v", err)
}

2.8.2 ErrGroup کی عملی استعمال

سوچیں ایک سیناریو جہاں ہمیں تین مختلف ڈیٹا سورسز سے ڈیٹا کو بہتریب سے فیچ کرنا ہے اور اگر ان میں سے کوئی ڈیٹا سورس ناکام ہوجاتا ہے، تو ہم چاہتے ہیں کہ باقی ڈیٹا فیچ کرنے کے عملات فوراً منسوخ ہوجائیں۔ یہ کام ErrGroup کا استعمال کرکے آسانی سے کیا جا سکتا ہے:

package main

import (
    "fmt"
    "golang.org/x/sync/errgroup"
)

func fetchDataFromSource1() error {
    // سورس 1 سے ڈیٹا فیچ کرنا سمیٹ کریں
    return nil // یا ناکامی کا بیان کرنے کے لئے کوئی خرابی لوٹائیں
}

func fetchDataFromSource2() error {
    // سورس 2 سے ڈیٹا فیچ کرنا سمیٹ کریں
    return nil // یا ناکامی کا بیان کرنے کے لئے کوئی خرابی لوٹائیں
}

func fetchDataFromSource3() error {
    // سورس 3 سے ڈیٹا فیچ کرنا سمیٹ کریں
    return nil // یا ناکامی کا بیان کرنے کے لئے کوئی خرابی لوٹائیں
}

func main() {
    var g errgroup.Group

    g.Go(fetchDataFromSource1)
    g.Go(fetchDataFromSource2)
    g.Go(fetchDataFromSource3)

    // تمام گوروٹین کے مکمل ہونے کا انتظار کریں اور ان کی خرابیوں کو جمع کریں
    if err := g.Wait(); err != nil {
        fmt.Printf("ڈیٹا فیچ کرتے وقت خرابی واقع ہوگئی: %v\n", err)
        return
    }

    fmt.Println("تمام ڈیٹا کامیابی سے فیچ ہوگئی!")
}

اس مثال میں، fetchDataFromSource1، fetchDataFromSource2 اور fetchDataFromSource3 فنکشنز مختلف ڈیٹا سورسز سے ڈیٹا فیچ کرنا نقلی کرتے ہیں۔ انہیں g.Go میں منتقل کیا گیا ہے اور علیحدہ گوروٹینز میں ان کا اجراء ہوتا ہے۔ اگر ان میں سے کوئی بھی فنکشن خرابی واپس کرتا ہے، تو g.Wait فوراً اس خرابی کو واپس کرے گا، جس سے خوراک شکایت کی جاسکتی ہے جب خرابی واقع ہوتی ہے۔ اگر تمام فنکشنز کامیابی سے اجراء ہوتے ہیں، تو g.Wait nil کو واپس دے گا، جو ظاہر کرتا ہے کہ تمام تسک کامیابی سے مکمل ہوگئے ہیں۔

ErrGroup کا ایک اور اہم خصوصیت یہ ہے کہ اگر کوئی بھی گوروٹین پینک ہوجاتا ہے، تو یہ اس پینک کو دوبارہ حاصل کرنے کی کوشش کرے گا اور اسے خرابی کے طور پر واپس کرے گا۔ یہ دوسرے برابر چل رہے ہونے والے گوروٹینز کو نرمی سے بند کرنے سے بچاتا ہے۔ بالکل، اگر آپ چاہتے ہیں کہ تسکات کو بیرونی منسوخ سگنلز کے ردعمل کے لئے تیار کریں، تو آپ errgroup کے WithContext فنکشن کو کنٹیکسٹ پیکیج کے ساتھ ملا کر منسوخ کیا جاسکتا ہے۔

اس طریقے سے، ErrGroup ہو گا کہ گو کی ہرگز مختلف تسکات کی عملی بنائی جانے والی سینکرونائزشن اور خرابی کا عمل۔