30. Channel

Channel digunakan untuk menghubungkan gorutine satu dengan goroutine lain. Mekanismenya adalah serah-terima data lewat channel tersebut. Goroutine pengirim dan penerima harus berada pada channel yang berbeda (konsep ini disebut buffered channel). Pengiriman dan penerimaan data pada channel bersifat blocking atau synchronous.

Analogi channel

Pada bab ini kita akan belajar mengenai pemanfaatan channel.

30.1. Penerapan Channel

Channel merupakan sebuah variabel, dibuat dengan menggunakan keyword make dan chan. Variabel channel memiliki tugas menjadi pengirim dan penerima data.

Program berikut adalah contoh implementasi channel. 3 buah goroutine dieksekusi, di masing-masing goroutine terdapat proses pengiriman data lewat channel. Data tersebut akan diterima 3 kali di goroutine utama main.

package main

import "fmt"
import "runtime"

func main() {
    runtime.GOMAXPROCS(2)

    var messages = make(chan string)

    var sayHelloTo = func(who string) {
        var data = fmt.Sprintf("hello %s", who)
        messages <- data
    }

    go sayHelloTo("john wick")
    go sayHelloTo("ethan hunt")
    go sayHelloTo("jason bourne")

    var message1 = <-messages
    fmt.Println(message1)

    var message2 = <-messages
    fmt.Println(message2)

    var message3 = <-messages
    fmt.Println(message3)
}

Pada kode di atas, variabel messages dideklarasikan bertipe channel string. Cara pembuatan channel yaitu dengan menuliskan keyword make dengan isi keyword chan diikuti dengan tipe data channel yang diinginkan.

var messages = make(chan string)

Selain itu disiapkan juga closure sayHelloTo yang menghasilkan data string. Data tersebut dikirim lewat channel messages. Tanda <- jika dituliskan di sebelah kiri nama variabel, berarti sedang berlangsung proses pengiriman data dari variabel yang berada di kanan lewat channel yang berada di kiri (pada konteks ini, variabel data dikirim lewat channel messages).

var sayHelloTo = func(who string) {
    var data = fmt.Sprintf("hello %s", who)
    messages <- data
}

Fungsi sayHelloTo dieksekusi tiga kali sebagai goroutine berbeda. Menjadikan tiga proses ini berjalan secara asynchronous atau tidak saling tunggu.

go sayHelloTo("john wick")
go sayHelloTo("ethan hunt")
go sayHelloTo("jason bourne")

Dari ketiga fungsi tersebut, goroutine yang selesai paling awal akan mengirim data lebih dulu, datanya kemudian diterima 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.

var message1 = <-messages
fmt.Println(message1)

Penerimaan 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, message3; untuk kemudian ditampilkan.

Implementasi channel

Dari screenshot output di atas bisa dilihat bahwa text yang dikembalikan oleh sayHelloTo tidak selalu berurutan, meskipun penerimaan datanya adalah berurutan. Hal ini dikarenakan, pengiriman data adalah dari 3 goroutine yang berbeda, yang kita tidak tau mana yang prosesnya selesai lebih dulu. Goroutine yang dieksekusi lebih awal belum tentu selesai lebih awal, yang jelas proses yang selesai lebih awal datanya akan diterima lebih awal.

Karena pengiriman dan penerimaan data lewat channel bersifat blocking, tidak perlu memanfaatkan sifat blocking dari fungsi fmt.Scanln() untuk mengantisipasi goroutine utama main selesai sebelum 3 goroutine di atas selesai.

30.2. Channel Sebagai Tipe Data Parameter

Variabel channel bisa di-passing ke fungsi lain sebagai parameter. Caranya dengan menambahkan keyword chan ketika deklarasinya.

Siapkan fungsi printMessage dengan parameter adalah channel. Lalu ambil data yang dikirimkan lewat channel tersebut untuk ditampilkan.

func printMessage(what chan string) {
    fmt.Println(<-what)
}

Setelah itu ubah implementasi di fungsi main.

func main() {
    runtime.GOMAXPROCS(2)

    var messages = make(chan string)

    for _, each := range []string{"wick", "hunt", "bourne"} {
        go func(who string) {
            var data = fmt.Sprintf("hello %s", who)
            messages <- data
        }(each)
    }

    for i := 0; i < 3; i++ {
        printMessage(messages)
    }
}

Parameter what fungsi printMessage bertipe channel string, bisa dilihat dari kode chan string pada cara deklarasinya. 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.

Parameter channel


Berikut merupakan penjelasan tambahan kode di atas.

30.3. Iterasi Data Array Langsung Pada Saat Inisialisasi

Data array yang baru di-inisialisasi bisa langsung di-iterasi, caranya mudah dengan menuliskanya langsung setelah keyword range.

for _, each := range []string{"wick", "hunt", "bourne"} {
    // ...
}

30.4. Eksekusi Goroutine Pada IIFE

Eksekusi goroutine tidak harus pada fungsi atau closure yang sudah terdefinisi. Sebuah IIFE juga bisa dijalankan sebagai goroutine baru. Caranya dengan langsung menambahkan keyword go pada waktu deklarasi-eksekusi IIFE-nya.

var messages = make(chan string)

go func(who string) {
    var data = fmt.Sprintf("hello %s", who)
    messages <- data
}("wick")

var message = <-messages
fmt.Println(message)

results matching ""

    No results matching ""