Proses, Thread, dan Goroutine

·Abdul Wahid Kahar

Mulai dari analogi

Bayangkan sebuah restoran.

  • Restoran = program yang berjalan di komputer

  • Dapur = memory (RAM) yang dipakai program

  • Pelayan = yang handle request dari customer

Sekarang ada 100 customer datang bersamaan. Gimana restorannya handle ini?


Proses

Proses = satu program yang sedang berjalan, punya memory sendiri yang terpisah.

Analoginya: membuka restoran cabang baru untuk tiap customer. Tiap cabang punya dapur sendiri, pelayan sendiri, peralatan sendiri.

Proses A → punya RAM sendiri Proses B → punya RAM sendiri (tidak bisa akses RAM proses A) Proses C → punya RAM sendiri

Masalahnya:

  • Buka cabang baru itu mahal dan lambat

  • Kalau ada 1000 customer, kamu perlu 1000 cabang — tidak realistis

Di dunia nyata: Browser Chrome membuka setiap tab sebagai proses terpisah. Makanya kalau satu tab crash, tab lain tidak ikut crash — tapi makanya juga Chrome makan banyak RAM.


Thread

Thread = unit kerja di dalam satu proses, berbagi memory yang sama.

Analoginya: satu restoran, tapi punya banyak pelayan. Semua pelayan pakai dapur yang sama, bahan yang sama.

Proses (1 restoran) ├── Thread 1 (pelayan 1) → handle customer A ├── Thread 2 (pelayan 2) → handle customer B └── Thread 3 (pelayan 3) → handle customer C

Lebih efisien dari proses — tidak perlu buka cabang baru, cukup tambah pelayan.

Tapi ada masalah baru: Karena semua pelayan pakai dapur yang sama — kalau dua pelayan ambil bahan yang sama di waktu bersamaan, bisa tabrakan. Ini namanya race condition.

Contoh nyata:

Thread 1: baca stok produk → 10 Thread 2: baca stok produk → 10 Thread 1: kurangi stok → simpan 9 Thread 2: kurangi stok → simpan 9 ← harusnya 8, tapi jadi 9

Data corrupt. Dan bug ini susah direproduksi karena tergantung timing.

Masalah lain: Setiap thread makan memory sekitar 1MB. Kalau ada 10.000 request bersamaan = 10GB RAM hanya untuk thread. Tidak scalable.


Goroutine

Goroutine = versi Go dari thread, tapi jauh lebih ringan.

Analoginya: pelayan yang bisa multitasking. Sambil nunggu pesanan customer A dimasak, dia langsung layani customer B. Tidak perlu nganggur.

Goroutine 1 → handle request A ↓ nunggu query database (sambil nunggu, Go switch ke goroutine lain) Goroutine 2 → handle request B ↓ nunggu response API (sambil nunggu, Go switch lagi) Goroutine 1 → database sudah selesai, lanjut proses

Kenapa goroutine lebih efisien dari thread?

ThreadGoroutineMemory per unit1MB2KBDibuat olehOSGo runtimeBiaya buat baruMahalSangat murah10.000 unit10GB RAM20MB RAM

Goroutine 500x lebih hemat memory dari thread.


Cara pakai goroutine di Go

go
func handleRequest(userID int) {
    // proses request
    fmt.Println("handling user", userID)
}

func main() {
    // tanpa goroutine — sequential, satu per satu
    handleRequest(1)
    handleRequest(2)
    handleRequest(3)

    // dengan goroutine — semua jalan bersamaan
    go handleRequest(1)
    go handleRequest(2)
    go handleRequest(3)
}

Tinggal tambah kata go di depan — fungsi itu langsung jalan di goroutine terpisah.


Tapi goroutine juga punya masalah yang sama dengan thread

Karena goroutine berbagi memory yang sama, race condition tetap bisa terjadi.

Go punya dua cara untuk handle ini:

1. Channel — goroutine komunikasi lewat "pipa"

go
ch := make(chan int)

go func() {
    ch <- 42 // kirim data ke channel
}()

nilai := <-ch // terima data dari channel
fmt.Println(nilai) // 42

Analoginya: pelayan tidak langsung ambil bahan dari dapur — mereka pesan lewat intercom, dan hanya satu yang bisa pesan di satu waktu.

2. Mutex — kunci pintu dapur

go
var mu sync.Mutex
stok := 10

go func() {
    mu.Lock()   // kunci dapur
    stok -= 1   // ambil bahan
    mu.Unlock() // buka kunci
}()

Hanya satu goroutine yang bisa masuk "dapur" di satu waktu — yang lain nunggu di luar.


Hubungannya ke backend Go kamu

Setiap request yang masuk ke server Go otomatis dihandle di goroutine terpisah:

Request 1 masuk → Go buat goroutine 1 Request 2 masuk → Go buat goroutine 2 Request 3 masuk → Go buat goroutine 3

Ini yang bikin Go sangat cocok untuk backend yang handle banyak request bersamaan — karena goroutine murah dan efisien.

Tapi ini juga kenapa kamu harus hati-hati:

  • Kalau dua goroutine akses variable yang sama tanpa proteksi → race condition → data corrupt

  • Kalau goroutine tidak pernah selesai → memory leak → server lambat lama-lama


Ringkasan

Proses → program terpisah, memory terpisah, berat Thread → dalam satu proses, berbagi memory, lebih ringan tapi masih berat Goroutine → seperti thread tapi 500x lebih ringan, dikelola Go runtime Race condition → bug yang terjadi kalau dua goroutine akses data yang sama bersamaan Channel → cara goroutine komunikasi dengan aman Mutex → cara goroutine "antri" akses data yang sama