परिचय
- हम दुनिया के पहले 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 टिप्पणियां
Hacker News राय
एक वैज्ञानिक के रूप में, जब पेशेवर software engineers के साथ काम करता हूँ, तो वे जो बहुत-सी चीज़ें करते हैं, वह समझ नहीं आती
कम मेहनत वाला, गैर-गंभीर comment छोड़ने का मन है
पुराने programming jokes, जो उस दौर के हैं जब C और उससे निकली भाषाएँ हावी थीं, आज भी काम करते हैं
इससे Buena Vista Social Club का classical music याद आ गया
मैंने कुछ खास स्थितियों में
chan chan Valueयाchan struct{resp chan Value}pattern का इस्तेमाल किया हैchannel का channel एक आम pattern है, और आमतौर पर यह struct type के field के channel होने के रूप में दिखता है
type request struct { params, reply chan response }जैसी shapedynamic dispatch mechanism को implement करने के लिए channels के इस्तेमाल के विरोध में एक blog
इससे Joe Armstrong का "My favorite Erlang Program" याद आता है
link पर click करते समय कुछ और उम्मीद की थी
LabVIEW code में asynchronous response data पाने के लिए इसी तरह का तरीका इस्तेमाल किया है