Channel adalah saluran bertipe untuk mengirim nilai dari satu goroutine ke goroutine lain. Bayangin antrean pesanan antara kasir dan dapur Warung Bakso Mas Bimo. Kasir (satu goroutine) memasukkan pesanan, dapur (goroutine lain) mengambilnya. Ada dua jenis antrean: tanpa nampan, kasir harus menyerahkan mangkuk langsung ke tangan juru masak, jadi keduanya harus ketemu dulu; dengan nampan berisi 3 tempat, kasir boleh menaruh sampai 3 pesanan lalu kembali ke pembeli. Itu channel unbuffered dan buffered. Prinsip Go: "jangan berkomunikasi dengan berbagi memori, bagilah memori dengan berkomunikasi".
🧪 Tebakan cepat:
antrean := make(chan string), lalumainlangsung menjalankanantrean <- "bakso urat"tanpa ada goroutine lain. Apa yang terjadi? Tebak dulu, jawabannya ada di bagian "Unbuffered: harus ketemu".
Membuat, mengirim, menerima
package main
import "fmt"
func main() {
antrean := make(chan string)
go func() {
antrean <- "bakso urat"
}()
pesanan := <-antrean
fmt.Println("dapur terima:", pesanan)
}
$ go run bakso.go
dapur terima: bakso urat
make(chan string)membuat channel yang mengangkut nilaistring. Channel juga punya tipe:chan intcuma menerimaint.- Operator
<-memindahkan nilai, dan arah panahnya menentukan artinya.antrean <- "bakso urat"mengirim ke channel;pesanan := <-antreanmenerima darinya. Jadi untukch := make(chan int), mengirim 42 ditulisch <- 42. - Menerima juga menunggu:
maintertahan di<-antreansampai goroutine kasir mengirim. Karena itu channel sekaligus jadi cara menunggu goroutine, selain WaitGroup.
Unbuffered: harus ketemu
Channel dari make(chan string) gak punya tempat menyimpan. Pengirim menunggu sampai ada penerima, dan penerima menunggu sampai ada pengirim. Begitu keduanya siap, nilainya pindah, dan dua-duanya lanjut.
Di tebakan tadi, satu-satunya goroutine adalah main, dan ia sendiri yang menunggu di baris kirim. Gak ada yang akan pernah menerima:
$ go run bakso.go
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [chan send]:
main.main()
/home/bimo/bakso/bakso.go:7 +0x28
exit status 2
Deadlock: semua goroutine saling menunggu dan gak ada yang bisa jalan. Runtime Go mendeteksinya, menyebut goroutine yang macet beserta alasannya (chan send), lalu menghentikan program. Itu jawaban tebakannya.
Buffered: nampan berkapasitas
antrean := make(chan string, 3)
antrean <- "bakso urat"
antrean <- "mie ayam"
antrean <- "es teh"
fmt.Println("isi:", len(antrean), "kapasitas:", cap(antrean)) // isi: 3 kapasitas: 3
antrean <- "bakso urat"
- Angka kedua di
makeadalah kapasitas buffer. Pengirim baru menunggu kalau buffer-nya penuh; penerima menunggu kalau kosong. - Tiga kiriman pertama langsung masuk walau belum ada yang menerima.
lenmemberi jumlah isi sekarang,capkapasitasnya. - Kiriman keempat menunggu tempat kosong. Di sini gak ada goroutine lain, jadi hasilnya sama:
fatal error: all goroutines are asleep - deadlock!dengangoroutine 1 [chan send].
make(chan string) |
make(chan string, 3) |
|
|---|---|---|
| Kirim menunggu kalau | belum ada penerima | buffer penuh (3 isi) |
| Terima menunggu kalau | belum ada pengirim | buffer kosong |
| Cocok untuk | titik temu, sinyal "sudah diterima" | menaruh dulu, sampai batas tertentu |
close dan range
Pengirim menutup channel dengan close saat sudah gak ada yang mau dikirim. Penerima bisa memakai range untuk mengambil semua isinya sampai channel ditutup:
func kasir(antrean chan<- string) {
for _, menu := range []string{"bakso urat", "mie ayam", "es teh"} {
fmt.Println("kasir kirim:", menu)
antrean <- menu
}
close(antrean)
}
func main() {
antrean := make(chan string)
go kasir(antrean)
for menu := range antrean {
fmt.Println("dapur masak:", menu)
}
fmt.Println("antrean kosong, dapur istirahat")
}
$ go run bakso.go
kasir kirim: bakso urat
kasir kirim: mie ayam
dapur masak: bakso urat
dapur masak: mie ayam
kasir kirim: es teh
dapur masak: es teh
antrean kosong, dapur istirahat
antrean chan<- stringdi parameter artinyakasircuma boleh mengirim ke channel itu.close(antrean)dipanggil kasir sesudah pesanan terakhir.- Urutan baris
kasir kirimdandapur masakbisa beda tiap kali jalan, karena dua goroutine mencetak bergantian. Urutan pesanannya tetap: bakso urat, mie ayam, es teh. for menu := range antreanberhenti sendiri begitu channel ditutup dan isinya habis. Tanpaclose,rangemenunggu selamanya dan berakhir dengan deadlock.- Menerima dari channel yang sudah ditutup dan kosong gak menunggu: hasilnya zero value tipenya. Pakai bentuk dua nilai untuk membedakannya:
m, ok := <-antreanmemberim=""danok=false. Kalaufmt.Printf("%q %v\n", m, ok), tercetak"" false.
📌 Arah channel di parameter
func kasir(antrean chan<- string) artinya kasir cuma boleh mengirim. <-chan string berarti cuma boleh menerima. Compiler menolak kalau dilanggar: menerima dari chan<- string gagal dengan invalid operation: cannot receive from send-only channel antrean (variable of type chan<- string). Ini membuat peran tiap fungsi jelas dari tanda tangannya.
⚠️ Jebakan yang sering ketemu
- Kirim di main tanpa penerima. Channel tanpa buffer langsung deadlock. Pastikan ada goroutine yang menerima, atau pakai buffer.
- Lupa
closesebelumrange. Penerimanya menunggu selamanya:goroutine 1 [chan receive]dan deadlock. - Kirim ke channel yang sudah ditutup. Program panic dengan
panic: send on closed channel. Menutup dua kali juga panic:panic: close of closed channel. Yang menutup selalu pengirim, sekali saja. - Menutup channel sebagai tanda wajib. Channel gak harus ditutup. Tutup hanya kalau penerima perlu tahu "sudah gak ada lagi", misalnya untuk
range.
TL;DR: Channel mengirim nilai antar goroutine: antrean := make(chan string), kirim dengan antrean <- "bakso urat", terima dengan m := <-antrean. Unbuffered (make(chan T)): pengirim menunggu sampai penerima siap. Buffered (make(chan T, 3)): pengirim baru menunggu kalau buffer penuh. Kalau semua goroutine saling menunggu, program mati dengan fatal error: all goroutines are asleep - deadlock!. Pengirim menutup channel dengan close, range membaca sampai channel ditutup, dan menerima dari channel tertutup yang kosong memberi zero value dengan ok == false. Kirim ke channel tertutup atau close dua kali bikin panic.