1 पॉइंट द्वारा GN⁺ 2024-08-29 | 1 टिप्पणियां | WhatsApp पर शेयर करें

परिचय

  • हम दुनिया के पहले version-controlled SQL database, Dolt, को Go भाषा में लिख रहे हैं
  • ज़्यादातर Go codebase की तरह, हम concurrent execution को लागू करने के लिए channels और goroutines का उपयोग करते हैं
  • आम तौर पर concurrent programming कठिन होती है, इसलिए हम सरल और सहज तरीकों का उपयोग करते हैं
  • लेकिन हमें दूसरे open source project से ऐसा code विरासत में मिला जिसमें channels का बेहद रचनात्मक तरीके से उपयोग किया गया था
var c chan chan struct{}
  • यह दूसरे goroutines के बीच channels पास करने का तरीका है, जिससे worker goroutines के बीच fan-out pattern लागू होता है
  • इस तरीके को समझना कठिन था, और goroutine leak को देखते हुए इस पर काम करना भी मुश्किल था
  • आखिरकार हमने इस code को फिर से लिखा और chan chan struct{} को हटा दिया

यह क्यों किया जाता है

  • उस दौर का एक पुराना programming joke है जब C और उससे निकली भाषाओं का दबदबा था
  • बहुत से लोगों को pointers समझने में कठिनाई होती थी
  • Go भी C से निकली भाषा होने के कारण वही काम कर सकती है
func main() {
  i := 1
  setInt(&i)
  fmt.Printf("i is now %d", i)
}

func setInt(i *int) {
  setInt2(&i)
}

func setInt2(i **int) {
  setInt3(&i)
}

func setInt3(i ***int) {
  setInt4(&i)
}

func setInt4(i ****int) {
  ****i = 100
}
  • यह code compile होता है और i is now 100 प्रिंट करता है
  • Go में channels का उपयोग करके भी वही काम किया जा सकता है

4-chan Go प्रोग्रामर

  • हम ऐसा program लिखेंगे जो channels की 4-स्तरीय indirect reference का उपयोग करता है
  • सबसे ऊपर वाले channel को 4-chan के रूप में declare किया जाता है
_4chan := make(chan chan chan chan int)
  • इस channel पर भेजी जाने वाली value 3-chan है
_3chan := make(chan chan chan int)
  • indirect reference के हर चरण पर एक निश्चित branching factor के आधार पर producer बनाए जाते हैं
func sendChanChanChan(c chan chan chan chan int) {
  for range factor {
    go func() {
      logrus.Debug("starting 3chan producer")
      _3chan := make(chan chan chan int)
      sendChanChan(c, _3chan)
    }()
  }
}
  • consumer भी इसी तरह काम करता है
func receiveChanChanChan(c chan chan chan chan int) {
  for _3chan := range c {
    logrus.Debug("got message from 4chan")
    for range factor {
      logrus.Debug("starting 3chan consumer")
      go receiveChanChan(_3chan)
    }
  }
}
  • अंत में हम उस चरण तक पहुँचते हैं जहाँ वास्तविक value भेजी जाती है
func send(_2chan chan chan int, _1chan chan int) {
  _2chan <- _1chan
  for range factor {
    go func() {
      logrus.Debug("starting int producer")
      for range factor {
        go func() {
          logrus.Debug("sending int")
          _1chan <- 1
        }()
      }
    }()
  }
}
  • consumer प्राप्त values का sum करता है
var sum = &atomic.Int32{}

func receive(c chan int) {
  for s := range c {
    logrus.Debug("received int")
    sum.Add(int32(s))
  }
}
  • अब सब कुछ जोड़कर इसे चलाते हैं
const factor = 3
var sum = &atomic.Int32{}

func main() {
  // logrus.SetLevel(logrus.DebugLevel)
  _4chan := make(chan chan chan chan int)
  go sendChanChanChan(_4chan)
  go receiveChanChanChan(_4chan)
  time.Sleep(500 * time.Millisecond)
  fmt.Printf("%d ^ 5: %d", factor, sum.Load())
}
  • यह program संख्या की 5वीं घात को संभवतः सबसे अधिक distributed तरीके से गणना करता है

टिप्पणी

  • वास्तविक code में ऐसा नहीं करना चाहिए, इसके कई कारण हैं: implementation और debugging की कठिनाई, self-respect का सवाल, सहकर्मियों की आलोचना आदि
  • लेकिन यह बेहद मज़ेदार है और काम भी करता है, इसलिए दिलचस्प है
  • एक व्यावहारिक कारण यह भी है कि जब channels को channel के रूप में भेजा जाता है, तो उन्हें बंद करना बहुत कठिन हो जाता है

निष्कर्ष

  • अगर Go में दिलचस्प concurrency pattern के बारे में आपके सवाल या विचार हों, तो आप Discord पर हमारी टीम और अन्य Dolt उपयोगकर्ताओं से बात कर सकते हैं

GN⁺ का सारांश

  • यह लेख Go भाषा में channels का उपयोग करने वाले एक रचनात्मक concurrency pattern पर चर्चा करता है
  • वास्तविक code में उपयोग के लिए यह अक्षम है, लेकिन अवधारणात्मक रूप से दिलचस्प है
  • यह दिखाता है कि Dolt जैसे project में Go की concurrency सुविधाओं का कैसे उपयोग किया जा सकता है
  • समान कार्यक्षमता वाले project में PostgreSQL, MySQL आदि शामिल हैं

1 टिप्पणियां

 
GN⁺ 2024-08-29
Hacker News राय
  • एक वैज्ञानिक के रूप में, जब पेशेवर software engineers के साथ काम करता हूँ, तो वे जो बहुत-सी चीज़ें करते हैं, वह समझ नहीं आती

    • मैंने एक लाइन के code को 4 "interface functions" से होकर call होते देखा है
    • हर function अलग file और folder में होता है, जिससे code पढ़ना बहुत थकाऊ हो जाता है
    • कुछ स्तर अंदर जाने के बाद सोचने लगता हूँ कि क्या मैं कभी उस हिस्से तक पहुँचूँगा जो वास्तव में calculation करता है
  • कम मेहनत वाला, गैर-गंभीर comment छोड़ने का मन है

    • शुरुआती कुछ paragraphs के memes, एक C programmer के रूप में, मुझे मज़ेदार लगे
    • language के अजीब variants देखना पसंद है, और Go में यह देखना दिलचस्प है
  • पुराने programming jokes, जो उस दौर के हैं जब C और उससे निकली भाषाएँ हावी थीं, आज भी काम करते हैं

  • इससे Buena Vista Social Club का classical music याद आ गया

  • मैंने कुछ खास स्थितियों में chan chan Value या chan struct{resp chan Value} pattern का इस्तेमाल किया है

    • इसकी जगह message bus इस्तेमाल कर सकता था, लेकिन फिर message bus को संभालने की स्थिति आ जाती
  • channel का channel एक आम pattern है, और आमतौर पर यह struct type के field के channel होने के रूप में दिखता है

    • request भेजना, फिर worker के काम पूरा करने के बाद result को response channel में डालना
    • type request struct { params, reply chan response } जैसी shape
    • दो channels उपयोगी होते हैं, और तीन या उससे ज़्यादा channels मैंने नहीं देखे
  • dynamic dispatch mechanism को implement करने के लिए channels के इस्तेमाल के विरोध में एक blog

    • Limbo language में इसका इस्तेमाल होता है, और concept Go जैसा ही है
    • ब्लॉग लिंक
  • इससे Joe Armstrong का "My favorite Erlang Program" याद आता है

  • link पर click करते समय कुछ और उम्मीद की थी

    • मैं Go programmer नहीं हूँ, इसलिए joke तुरंत समझ नहीं आया
  • LabVIEW code में asynchronous response data पाने के लिए इसी तरह का तरीका इस्तेमाल किया है

    • response को queue में dump करने के बजाय, callback event channel शामिल करने वाला message पास किया
    • memory की बर्बादी होती है, लेकिन एक बार इस्तेमाल के बाद response पर बंद हो जाता है, इसलिए efficient है