Goroutine mirip dengan thread, tapi sebenarnya bukan. Sebuah native thread bisa berisikan sangat banyak goroutine. Mungkin lebih pas kalau goroutine disebut sebagai mini thread. Goroutine sangat ringan, hanya dibutuhkan sekitar 2kB memori saja untuk satu buah goroutine. Eksekusi goroutine bersifat asynchronous, menjadikannya tidak saling tunggu dengan goroutine lain.
Karena goroutine sangat ringan, maka eksekusi banyak goroutine bukan masalah. Akan tetapi jika jumlah goroutine sangat banyak sekali (contoh 1 juta goroutine dijalankan pada komputer dengan RAM terbatas), memang proses akan jauh lebih cepat selesai, tapi memory/RAM pasti bengkak.
Goroutine merupakan salah satu bagian paling penting dalam concurrent programming di Go. Salah satu yang membuat goroutine sangat istimewa adalah eksekusi-nya dijalankan di multi core processor. Kita bisa tentukan berapa banyak core yang aktif, makin banyak akan makin cepat.
Mulai chapter A.29 ini hingga A.34, lalu dilanjut A.59 dan A.60, kita akan membahas tentang fitur-fitur yang disediakan Go untuk kebutuhan concurrent programming.
Concurrency atau konkurensi berbeda dengan paralel. Paralel adalah eksekusi banyak proses secara bersamaan. Sedangkan konkurensi adalah komposisi dari sebuah proses. Konkurensi merupakan struktur, sedangkan paralel adalah bagaimana eksekusinya berlangsung.
Untuk menerapkan goroutine, proses yang akan dieksekusi sebagai goroutine harus dibungkus ke dalam sebuah fungsi. Pada saat pemanggilan fungsi tersebut, ditambahkan keyword go
di depannya, dengan itu goroutine baru akan dibuat dengan tugas adalah menjalankan proses yang ada dalam fungsi tersebut.
Berikut merupakan contoh implementasi sederhana tentang goroutine. Program di bawah ini menampilkan 10 baris teks, 5 dieksekusi dengan cara biasa, dan 5 lainnya dieksekusi sebagai goroutine baru.
package main
import "fmt"
import "runtime"
func print(till int, message string) {
for i := 0; i < till; i++ {
fmt.Println((i + 1), message)
}
}
func main() {
runtime.GOMAXPROCS(2)
go print(5, "halo")
print(5, "apa kabar")
var input string
fmt.Scanln(&input)
}
Pada kode di atas, Fungsi runtime.GOMAXPROCS(n)
digunakan untuk menentukan jumlah core yang diaktifkan untuk eksekusi program.
Pembuatan goroutine baru ditandai dengan keyword go
. Contohnya pada statement go print(5, "halo")
, di situ fungsi print()
dieksekusi sebagai goroutine baru.
Fungsi fmt.Scanln()
mengakibatkan proses jalannya aplikasi berhenti di baris itu (blocking) hingga user menekan tombol enter. Hal ini perlu dilakukan karena ada kemungkinan waktu selesainya eksekusi goroutine print()
lebih lama dibanding waktu selesainya goroutine utama main()
, mengingat bahwa keduanya sama-sama asnychronous. Jika itu terjadi, goroutine yang belum selesai secara paksa dihentikan prosesnya karena goroutine utama sudah selesai dijalankan.
Bisa dilihat di output, tulisan "halo"
dan "apa kabar"
bermunculan selang-seling. Ini disebabkan karena statement print(5, "halo")
dijalankan sebagai goroutine baru, menjadikannya tidak saling tunggu dengan print(5, "apa kabar")
.
Pada gambar di atas, program dieksekusi 2 kali. Hasil eksekusi pertama berbeda dengan kedua, penyebabnya adalah karena kita menggunakan 2 prosesor. Goroutine mana yang dieksekusi terlebih dahulu tergantung kedua prosesor tersebut.
Berikut adalah penjelasan tambahan tentang beberapa fungsi yang baru kita pelajari di atas.
Fungsi ini digunakan untuk menentukan jumlah core atau processor yang digunakan dalam eksekusi program.
Jumlah yang diinputkan secara otomatis akan disesuaikan dengan jumlah asli logical processor yang ada. Jika jumlahnya lebih, maka dianggap menggunakan sejumlah prosesor yang ada.
Fungsi ini akan meng-capture semua karakter sebelum user menekan tombol enter, lalu menyimpannya pada variabel.
func Scanln(a ...interface{}) (n int, err error)
Kode di atas merupakan skema fungsi fmt.Scanln()
. Fungsi tersebut bisa menampung parameter bertipe interface{}
berjumlah tak terbatas. Tiap parameter akan menampung karakter-karakter inputan user yang sudah dipisah dengan tanda spasi. Agar lebih jelas, silakan perhatikan contoh berikut.
var s1, s2, s3 string
fmt.Scanln(&s1, &s2, &s3)
// user inputs: "trafalgar d law"
fmt.Println(s1) // trafalgar
fmt.Println(s2) // d
fmt.Println(s3) // law
Bisa dilihat pada kode di atas, untuk menampung inputan text trafalgar d law
, dibutuhkan 3 buah variabel. Juga perlu diperhatikan bahwa yang disisipkan sebagai parameter pada pemanggilan fungsi fmt.Scanln()
adalah referensi variabel, bukan nilai aslinya.