Pengenalan Channel Pada Golang
Channel adalah penghubung goroutine yang dari satu ke yang lainnya. Channel ini sifat-nya synchronous karena ada blocking. Channel bisa didefinisikan dengan bentuk variabel dengan keyword chan. Variabel ini memiliki tugas untuk mengirim dan menerima data.
Berikut contoh implementasi channel yang memiliki 3 goroutine baru dieksekusi dan masing-masing gouroutine melakukan proses data lewat channel. Data tersebut akan diterima 3 kali di goroutine utama (main).
1package main
2
3import (
4 "fmt"
5 "runtime"
6)
7
8func main() {
9 runtime.GOMAXPROCS(2)
10 var messages = make(chan string)
11 var sayHelloTo = func(who string) {
12 var data = fmt.Sprintf("hello %s", who)
13 messages <- data
14 }
15
16 go sayHelloTo("captain america")
17 go sayHelloTo("captain marvel")
18 go sayHelloTo("black panter")
19
20 var message1 = <-messages
21 fmt.Println(message1)
22
23 var message2 = <-messages
24 fmt.Println(message2)
25
26 var message3 = <-messages
27 fmt.Println(message3)
28}Pada kode di atas, variabel messages dideklarasikan bertipe channel string. Cara inisialisasi variabel channel yaitu dengan menuliskan make dengan keyword chan diikuti dengan tipe data channel yang diinginkan.
1var messages = make(chan string)closure sayHelloTo yang mengembalikan data string. Data tersebut kemudian dikirim lewat channel messages. Tanda <- jika dituliskan di sebelah kiri nama variabel maka proses tersebut sedang berlangsung proses pengiriman data dari variabel yang berada di kanan lewat channel yang berada dikiri. Pada konteks ini, variabel data dikirim lewat channel messages.1var sayHelloTo = func(who string) {
2 var data = fmt.Sprintf("hello %s", who)
3 messages <- data
4}sayHelloTo dieksekusi 3 kali sebagai goroutine berbeda. Menjadikan tiga proses ini berjalan secara asynchronous atau tidak saling tunggu.1go sayHelloTo("captain america")
2go sayHelloTo("captain marvel")
3go sayHelloTo("black panther")Dari ketiga fungsi tersebut, goroutine yang paling awal bertugas untuk mengirim data dan akan diterima datanya oleh variabel message1 . Tanda <- jika dituliskan di sebelah kanan channel, menandakan proses penerimaan data dari channel yang di kanan, untuk disimpan ke variabel yang di kiri.
1var message1 = <-messages
2fmt.Println(message1)Dikarenakan channel bersifat blocking artinya statement var message1 = <-messages hingga setelahnya tidak akan dieksekusi sebelum ada data yang dikirim lewat channel.
Ketiga goroutine tersebut datanya akan diterima secara berurutan oleh message1, message2 dan message3 kemudian ditampilkan.
1➜ channel git:(main) ✗ go run main.go
2hello black panter
3hello captain marvel
4hello captain america
5➜ channel git:(main) ✗ go run main.go
6hello black panter
7hello captain marvel
8hello captain america
9➜ channel git:(main) ✗ go run main.go
10hello black panter
11hello captain marvel
12hello captain americasayHelloTo tidak selalu berurutan, meskipun penerimaan datanya berurutan. Hal ini dikarenakan pengiriman data dari 3 goroutine yang berbeda, yang kita tidak tahu mana yang lebih dahulu dieksekusi. Goroutine yang dieksekusi lebih awal, datanya akan diterima lebih awal juga.Sudah disinggung di atas bahwa channel ini blocking sehingga tidak perlu menggunakan perintah fmt.Scanln() untuk melakukan blocking tersebut.
Channel Tipe Data Parameter
Variabel channel bisa di-passing ke fungsi lain sebagai parameter. Caranya dengan menambahkan keyword chan ketika melakukan deklarasinya.
Kita lanjutkan dengan mempraktekkan saja. Siapkan fungsi printMessage dengan parameter channel, lalu ambil data yang dikirimkan lewat channel tersebut untuk ditampilkan.
1func printMessage(what chan string) {
2 fmt.Println(<-what)
3}main. 1func main() {
2 runtime.GOMAXPROCS(2)
3 var messages = make(chan string)
4
5 for _, each := range []string{"america", "marvel", "panther"} {
6 go func(who string) {
7 var data = fmt.Sprintf("hello %s", who)
8 messages <- data
9 }(each)
10 }
11
12 for i := 0; i < 3; i++ {
13 printMessage(messages)
14 }
15}what pada fungsi printMessage bertipe channel string. Operasi serah-terima data akan bisa dilakukan pada variabel tersebut, dan akan berdampak juga pada variabel messages di fungsi main.Passing data bertipe channel lewat parameter secara implisit adalah pass by reference, yang di-passing adalah pointer-nya. Output program di atas adalah sama dengan program sebelumnya.
1➜ channel-parameter git:(main) ✗ go run main.go
2hello america
3hello marvel
4hello panther
5➜ channel-parameter git:(main) ✗ go run main.go
6hello panther
7hello marvel
8hello americaKita akan kupas tuntas program di atas bisa berjalan.
Iterasi Data Array Pada Inisialisasi
Data array yang baru di-inisialisasi bisa langsung diiterasi, caranya mudah dengan menuliskanya langsung setelah keyword range.
1for _, each := range []string{"america", "marvel", "panther"} {
2 // ...
3}Buffered Channel
Channel secara default adalah un-buffered, tidak di-buffer di memori. Ketika ada goroutine yang mengirimkan data lewat channel, harus ada goroutine lain yang bertugas menerima data dari channel yang sama dengan proses serah-terima yang bersifat blocking. Maksudnya, baris kode di bagian pengiriman dan penerimaan data, tidak akan akan diproses sebelum proses serah-terima-nya selesai.
Buffered channel sedikit berbeda. Pada channel jenis ini, ditentukan jumlah buffer-nya. Angka tersebut akan menjadi penentu kapan kita bisa mengirimkan data. Selama jumlah data yang dikirim tidak melebihi jumlah buffer, maka pengiriman akan berjalan asynchronous (tidak blocking).
Ketika jumlah data yang dikirim sudah melewati batas buffer, maka pengiriman data hanya bisa dilakukan ketika salah satu data sudah diambil dari channel, sehingga ada slot channel yang kosong. Dengan proses penerimaan-nya sendiri bersifat blocking.
Berikut contoh penerapan Buffered Channel dengan membuat program bahwa pengiriman data lewat buffered channel adalah asynchronous selama jumlah data yang sedang di-buffer oleh channel tidak melebihi kapasitas buffernya.
1package main
2
3import (
4 "fmt"
5 "runtime"
6)
7
8func main() {
9 runtime.GOMAXPROCS(2)
10 messages := make(chan int, 2)
11 go func() {
12 for {
13 i := <-messages
14 fmt.Println("receive data", i)
15 }
16 }()
17 for i := 0; i < 5; i++ {
18 fmt.Println("send data", i)
19 messages <- i
20 }
21}make merepresentasikan jumlah buffere-nya. Nah, perlu kita perhatikan bahwa nilai buffered channel dimulai dari 0. Jadi, ketika nilainya adalah 2, berarti jumlah buffere maksimal ada 3 yaitu 0, 1, 2.Pada contoh di atas terdapat sebuah goroutine yang berisikan proses penerimaan data dari channel message yang selanjutnya akan ditampilkan.
Setelah goroutine penerima data dieksekusi, maka data akan dikirimkan lewat perulangan for. Pada program di atas kita akan mengirim sejumlah 5 data lewat channel message secara sekuensial.
1➜ channel-buffered git:(main) ✗ go run main.go
2send data 0
3send data 1
4send data 2
5send data 3
6receive data 0
7receive data 1
8receive data 2
9receive data 3
10send data 4Hasil dari output di atas kita bisa lihat bahwa pengiriman ke-4, diikuti dengan penerimaan data, dan kedua proses tersebut berjalan secara blocking. Kenapa seperti itu? Karena pada saat pengiriman data ke 0, 1, 2 dan 3 akan berjalan secara asynchronous, hal ini karena channal yang kita tentukan memiliki nilai buffer sebanyak 3 (dimulai dari 0).
Peingiriman selanjutnya (ke-4 dan ke-5) hanya akan terjadi jika ada salah satu data dari 4 data yang sebelumnya telah dikirimkan dan sudah diterima (serah terima data yang bersifat blocking). Sesudah slot channel kosong maka data ke-4 dan ke-5 akan di serah-terima kembali dengan aysnchronous.