Skip to content
Santekno.com | Level Up Your Engineering Skills
ID
📖 0%
08 Jan 2023 · 6 mnt baca ·Artikel 100 / 119
Go

Pengenalan Channel Pada Golang

IH
Ihsan Arif
Penulis di Santekno · Backend Engineer

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).

go
 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.

go
1var messages = make(chan string)
Pada program ini, kita buat juga fungsi 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.

go
1var sayHelloTo = func(who string) {
2    var data = fmt.Sprintf("hello %s", who)
3    messages <- data
4}
Fungsi sayHelloTo dieksekusi 3 kali sebagai goroutine berbeda. Menjadikan tiga proses ini berjalan secara asynchronous atau tidak saling tunggu.
go
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.

go
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.

bash
 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 america
Dapat dilihat bahwa output yang dikembalikan sayHelloTo 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.

go
1func printMessage(what chan string) {
2    fmt.Println(<-what)
3}
Setelah itu ubah beberapa baris untuk implementasi di fungsi main.
go
 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}
Parameter 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.

go
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 america

Kita 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.

go
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.

go
 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}
Pada kode di atas, parameter kedua fungsi 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.

bash
 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 4
Bisa dilihat hasilnya pada output di atas. Pengiriman data ke-4, diikuti dengan penerimaan data, dan kedua proses tersebut berjalan secara blocking. Pengiriman data ke 0, 1, 2 dan 3 akan berjalan secara asynchronous, hal ini karena channel ditentukan nilai buffer-nya sebanyak 3 (ingat, dimulai dari 0). Pengiriman selanjutnya (ke-4 dan ke-5) hanya akan terjadi jika ada salah satu data dari 4 data yang sebelumnya telah dikirimkan, sudah diterima (dengan serah terima data yang bersifat blocking). Setelahnya, sesudah slot channel ada yang kosong, serah-terima akan kembali asynchronous.

Hasil 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.

Artikel Terkait

💬 Komentar