Pengantar
- Kami menulis Dolt, database SQL dengan version control pertama di dunia, dalam bahasa Go
- Seperti kebanyakan codebase Go, kami menggunakan channel dan goroutine untuk mengimplementasikan eksekusi konkuren
- Secara umum, pemrograman konkuren itu sulit, jadi kami biasanya memakai cara yang sederhana dan intuitif
- Namun, kami mewarisi kode dari proyek open source lain yang menggunakan channel dengan cara yang sangat kreatif
var c chan chan struct{}
- Ini adalah cara meneruskan channel antar goroutine lain, untuk mengimplementasikan pola fan-out di antara goroutine pekerja
- Pendekatan ini sulit dipahami, dan juga sulit dikerjakan jika mempertimbangkan kebocoran goroutine
- Pada akhirnya kami menulis ulang kode ini dan menghapus
chan chan struct{}
Mengapa melakukan ini
- Ada lelucon pemrograman lama dari masa ketika bahasa C dan turunannya mendominasi
- Banyak orang kesulitan memahami pointer
- Karena Go juga merupakan bahasa turunan C, hal yang sama bisa dilakukan
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
}
- Kode ini dapat dikompilasi dan mencetak
i is now 100
- Di Go, hal yang sama juga bisa dilakukan dengan memakai channel
Programmer Go 4-chan
- Kita akan menulis program yang menggunakan 4 tingkat indireksi channel
- Channel tingkat teratas dideklarasikan sebagai 4-chan
_4chan := make(chan chan chan chan int)
- Nilai yang dikirim ke channel ini adalah 3-chan
_3chan := make(chan chan chan int)
- Pada setiap tingkat indireksi, kita membuat producer sesuai faktor percabangan tertentu
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 juga diproses dengan cara yang sama
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)
}
}
}
- Akhirnya kita mencapai tahap pengiriman nilai yang sebenarnya
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 menjumlahkan nilai yang diterima
var sum = &atomic.Int32{}
func receive(c chan int) {
for s := range c {
logrus.Debug("received int")
sum.Add(int32(s))
}
}
- Lalu semuanya digabungkan dan dijalankan
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 ini menghitung pangkat lima dari sebuah angka dengan cara yang semaksimal mungkin terdistribusi
Komentar
- Ada banyak alasan untuk tidak melakukan ini di kode nyata: sulit diimplementasikan dan di-debug, melukai harga diri, serta mengundang cibiran rekan kerja
- Namun tetap menarik karena sangat lucu dan benar-benar bekerja
- Salah satu alasan praktisnya adalah bahwa ketika channel dikirim sebagai channel, penutupannya menjadi sangat sulit
Kesimpulan
- Jika Anda punya pertanyaan atau pendapat tentang pola konkurensi Go yang menarik, Anda bisa berbicara dengan tim kami dan pengguna Dolt lainnya di Discord
Ringkasan GN⁺
- Artikel ini membahas pola konkurensi yang kreatif di bahasa Go dengan menggunakan channel
- Meskipun tidak efisien untuk dipakai di kode nyata, idenya menarik secara konseptual
- Artikel ini menunjukkan bagaimana fitur konkurensi Go dapat dimanfaatkan dalam proyek seperti Dolt
- Proyek dengan fungsi serupa antara lain PostgreSQL dan MySQL
1 komentar
Pendapat Hacker News
Sebagai ilmuwan, saat bekerja dengan insinyur perangkat lunak profesional, banyak hal yang mereka lakukan terasa tidak bisa dipahami
Ingin meninggalkan komentar bernilai rendah dengan usaha minim
Lelucon pemrograman lama dari masa ketika C dan bahasa turunannya mendominasi masih tetap berlaku
Mengingatkan pada musik klasik dari Buena Vista Social Club
Pernah memakai pola "chan chan Value" atau "chan struct{resp chan Value}" dalam situasi tertentu
Channel of channels adalah pola yang umum, dan biasanya muncul sebagai field bertipe channel di dalam struct
type request struct { params, reply chan response }Blog yang berpendapat sebaliknya tentang penggunaan channel untuk mengimplementasikan mekanisme dynamic dispatch
Mengingatkan pada "My favorite Erlang Program" milik Joe Armstrong
Saat mengklik tautannya, sempat mengharapkan sesuatu yang lain
Di kode LabVIEW, pernah memakai cara serupa untuk menerima data respons asinkron