گوروٹین کا انتظام

گوروٹینز ہلکے ہیں، مگر وہ بالکل مفت نہیں ہیں: کم ازکم، ان کے سٹیک اور سی پی یو شیڈولنگ کے لئے میموری استعمال ہوتی ہے۔ گوروٹینوں کی استعمال کے لئے یہ خرچ نہایت کم ہوتے ہیں، لیکن انہیں بے قابو زندگی کے بطور بڑی تعداد میں پیدا کرنے سے ناقابل۔ کنٹرول عملہ کرنے کا مظاہرا بھی ہو سکتا ہے۔ انتظامی دوری ختم ہونے والے گوروٹینز بھی، غیر استعمال ہونے والے اشیاء کو کچھ دنگ کرنے سے متعلق کی مسائل پیدا کر سکتے ہیں اور غیر استعمال ہونے والے وسائل کو جاری رکھنے کیلئے بی ضرورت وقت گزار سکتے ہیں۔

اس لئے، اپنے کوڈ میں گوروٹینز کو بہتر نہ لیک کریں۔ go.uber.org/goleak کا استعمال کریں تاکہ گوروٹین کی چھوٹی سی چوٹ پیدا ہونے والے پیکج کے اندر رسیدیں دیکھ سکے جو گوروٹین کو پیدا کرتے ہیں۔

عموماً، ہر گوروٹین کو:

  • پیش بین دورانیہ کا ہونا چاہئے، یا
  • گوروٹین کو بتانے کا طریقہ ہونا چاہئے کہ وہ رک جائے گی۔

اسے دونوں صورتوں میں کوڈ کے لئے رکنا اور گوروٹین کی مکمل ہونے کا انتظام ہونا چاہئے۔

مثال کے طور پر:

تجویز نہیں کی جاتی ہے:

go func() {
  for {
    flush()
    time.Sleep(delay)
  }
}()
// اس گوروٹین کو رکنے کا کوئی طریقہ نہیں ہے۔ یہ اطولی طور پر چلے گی جب تک کہ اپلیکیشن بند نہ ہو جائے۔

تجویز کی جاتی ہے:

var (
  stop = make(chan struct{}) // گوروٹین کو رکنے کا اشارہ
  done = make(chan struct{}) // اسے بتانے کا اشارہ ہے کہ گوروٹین مکمل ہو گئی ہے
)
go func() {
  defer close(done)
  ticker := time.NewTicker(delay)
  defer ticker.Stop()
  for {
    select {
    case <-ticker.C:
      flush()
    case <-stop:
      return
    }
  }
}()

// ... دیگر کوڈ
close(stop)  // اس بات کا تشویش ہے کہ گوروٹین رک جائے
<-done       // اور اس کے مکمل ہونے کا انتظار کریں

// اس گوروٹین کو close(stop) کی مدد سے انتظار کیا جا سکتا ہے، اور ہم اس کے مکمل ہونے کا منتظر ہوںگے۔

گوروٹین کا مکمل ہونے کا انتظار

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

  • sync.WaitGroup کا استعمال کرنا۔ اس ترکیب کا استعمال متعدد گوروٹینز کے لئے انتظار کرتے وقت کیلئے کریں۔
var wg sync.WaitGroup
for i := 0; i < num; i++ {
    wg.Add(1)
    go func() {
        defer wg.Done()
        // ...
    }()
}
wg.Wait()
  • دوسرا chan struct{} شامل کرنا، اور اس کو گوروٹین مکمل ہونے کے بعد بند کرنا۔ اس ترکیب کا استعمال اگر صرف ایک گوروٹین ہو تو کریں۔
done := make(chan struct{})
go func() {
    defer close(done)
    // ...
}()
// گوروٹین مکمل ہونے کا انتظار کرنے کے لئے:
<-done

init() فنکشن کو گوروٹینز پیدا نہیں کرنا چاہیئے۔ اس کے علاوہ، init() کا استعمال نہ کریں کا رجوع کریں۔

اگر پیکیج کو پس منظر کی گوروٹین درکار ہوتی ہے، تو اس کو گوروٹین کے دورانیے کا انتظام کرنے کے لئے ذمہ دار ایک اشیاء کو ظاہر کرنا چاہیئے۔ یہ اشیاء گوروٹین کے پشتہ منظر کو ختم کرنے اور اس کے مکمل ہونے کا انتظار کرنے کا فنکشن فراہم کرنا چاہئے۔ (Close، Stop، Shutdown، وغیرہ)

تجویز نہیں کی جاتی ہے:

func init() {
    go doWork()
}
func doWork() {
    for {
        // ...
    }
}
// جب یوزر اس پیکیج کو بیرونی کرتا ہے، تو بیک گراﺅنڈ گوروٹین بے معقول طور پر پیدا ہوتی ہے۔ یوزر کو گوروٹین کو نہیں کنٹرول کرنے یا اسے روک نہیں سکتا۔

تجویز کی جاتی ہے:

type Worker struct { /* ... */ }
func NewWorker(...) *Worker {
    w := &Worker{
        stop: make(chan struct{}),
        done: make(chan struct{}),
        // ...
    }
    go w.doWork()
}
func (w *Worker) doWork() {
    defer close(w.done)
    for {
        // ...
        select {
        case <-w.stop:
            return
        }
    }
}
// شٹ ڈاؤن کام کو بتاتا ہے کہ کام روک دیا جائے
// اور اس کے مکمل ہونے کا انتظار کرتا ہے۔
func (w *Worker) Shutdown() {
    close(w.stop)
    <-w.done
}

صرف اس وقت کارکردہ کام کو بناتے ہیں جب یوزر کی درخواست ہو اور یوزر کو صوابدیدی کرنے والے فنکشن کو بھی فراہم کرتے ہیں تاکہ یوزر کو وسائل بہتر کرنے کے لئے استعمال کرنے کا اشارہ ہو۔

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