Goroutine — Go

Goroutine Goroutine adalah lightweight thread yang dikelola oleh Go runtime. Membuat goroutine sangat murah — kamu bisa menjalankan ribuan bahkan jutaan…

Goroutine adalah fungsi yang jalan bersamaan dengan fungsi lain. Cara memulainya cukup menulis go di depan pemanggilan fungsi. Bayangin kasir Warung Bakso Mas Bimo. Tiap pesanan diserahkan ke juru masak di dapur, lalu kasir langsung melayani pembeli berikutnya tanpa menunggu masakan matang. Kasirnya main, tiap juru masak satu goroutine. Tapi ada satu aturan keras: begitu kasir pulang, warung tutup, dan semua juru masak ikut pulang, masakannya sudah matang atau belum.

🧪 Tebakan cepat: main memulai tiga goroutine masak, mencetak kasir: 3 pesanan masuk, lalu selesai. Berapa baris yang keluar di terminal? Tebak dulu, jawabannya ada di bagian "main gak menunggu".

Memulai goroutine dengan go

package main

import "fmt"

func masak(meja int, menu string) {
	fmt.Println("dapur: meja", meja, menu, "siap")
}

func main() {
	go masak(1, "bakso urat")
	go masak(2, "mie ayam")
	go masak(3, "es teh")
	fmt.Println("kasir: 3 pesanan masuk")
}

main gak menunggu

$ go run bakso.go
kasir: 3 pesanan masuk

Cuma satu baris. main mencetak baris kasir lalu selesai, dan begitu main selesai, program berhenti beserta semua goroutine-nya, sebelum satu pun sempat mencetak. Itu jawaban tebakannya. Di 8 kali percobaan di Go Playground hasilnya selalu satu baris ini, tapi di mesin lain kadang satu baris dapur sempat lolos. Gak ada yang dijamin.

time.Sleep cuma menebak

Menambah time.Sleep(100 * time.Millisecond) sebelum main selesai membuat ketiga baris dapur muncul, karena masaknya cepat. Tapi kalau tiap masakan butuh 200 ms:

$ go run bakso.go
kasir: 3 pesanan masuk
kasir: semua pesanan siap

Kasir bilang semua siap padahal belum ada yang matang. Sleep cuma menebak berapa lama harus menunggu, dan tebakannya bisa salah kapan saja.

sync.WaitGroup: tunggu sampai semua selesai

sync.WaitGroup adalah penghitung kerjaan yang belum selesai:

package main

import (
	"fmt"
	"sync"
)

func masak(meja int, menu string, wg *sync.WaitGroup) {
	defer wg.Done()
	fmt.Println("dapur: meja", meja, menu, "siap")
}

func main() {
	var wg sync.WaitGroup
	wg.Add(3)
	go masak(1, "bakso urat", &wg)
	go masak(2, "mie ayam", &wg)
	go masak(3, "es teh", &wg)
	fmt.Println("kasir: 3 pesanan masuk")
	wg.Wait()
	fmt.Println("kasir: semua pesanan siap")
}
$ go run bakso.go
kasir: 3 pesanan masuk
dapur: meja 3 es teh siap
dapur: meja 1 bakso urat siap
dapur: meja 2 mie ayam siap
kasir: semua pesanan siap

Urutannya gak tetap

Tiga baris dapur di atas keluar dengan urutan 3, 1, 2. Jalankan lagi, urutannya bisa lain. Dari 20 kali percobaan di Go Playground (8 CPU), urutan 3, 1, 2 paling sering muncul, tapi 1, 2, 3, lalu 2, 3, 1, 1, 3, 2, 3, 2, 1, dan 2, 1, 3 juga keluar. Goroutine dijadwalkan oleh runtime Go, dan urutan jalannya gak dijamin sama dengan urutan dibuat. Kalau urutan penting, sinkronkan secara eksplisit.

Goroutine berjalan concurrent: banyak kerjaan berjalan saling selang-seling. Di prosesor dengan banyak core, mereka juga bisa benar-benar jalan bersamaan (parallel).

Kenapa goroutine murah

Goroutine dimulai dengan stack (memori untuk pemanggilan fungsi) sekitar 2 KB yang membesar sendiri kalau perlu. Thread sistem operasi biasanya dipesan dengan stack berukuran MB. Goroutine juga dijadwalkan oleh runtime Go, bukan oleh sistem operasi: runtime membagi banyak goroutine ke sedikit thread (disebut penjadwalan M:N). Karena itu satu program Go bisa menjalankan ribuan, bahkan ratusan ribu, goroutine sekaligus.

Sejak Go 1.22, variabel loop dibuat baru di tiap putaran, jadi for i, menu := range pesanan { go func() { ... menu ... }() } aman: tiap goroutine memegang menu miliknya. Sejak Go 1.25 ada juga wg.Go(func() { ... }), yang sekaligus melakukan Add(1) dan Done().

⚠️ Jebakan yang sering ketemu

TL;DR: go f() menjalankan f di goroutine baru, dan pemanggilnya langsung lanjut. Program berhenti begitu main selesai, beserta semua goroutine yang belum beres, jadi tanpa menunggu kamu bisa cuma melihat kasir: 3 pesanan masuk. time.Sleep cuma menebak. Pakai sync.WaitGroup: wg.Add(n) sebelum go, defer wg.Done() di dalam goroutine, wg.Wait() di main, dan kirim &wg. Urutan goroutine gak tetap. Goroutine murah: stack awal sekitar 2 KB dan dijadwalkan runtime Go, bukan sistem operasi.

Yang akan kamu pelajari