Goroutine & Channel

Eksekusi bersamaan (concurrency) adalah salah satu kekuatan terbesar NusaScript yang diwarisi dari Go. Jalankan ribuan tugas secara paralel dengan mudah.

Apa itu Goroutine?

Goroutine adalah fungsi yang berjalan secara bersamaan (concurrent) dengan fungsi lain tanpa memblokir eksekusi program. Berbeda dengan thread OS yang berat, goroutine sangat ringan — Anda bisa menjalankan ribuan goroutine sekaligus.

KonsepGoNusaScript
Goroutinego fn()luncurkan(fn)
Jeda programtime.Sleep(n)tidur(ms)
Channel (buat)make(chan T, n)saluran_baru(n)
Kirim ke channelch <- valkirim(ch, val)
Terima dari channel<- chterima(ch)
Mutex baru&sync.Mutex{}kunci_baru()
Lockmu.Lock()kunci(mu)
Unlockmu.Unlock()buka_kunci(mu)
WaitGroup buat&sync.WaitGroup{}grup_tunggu()
WaitGroup Addwg.Add(n)tambah_tunggu(wg, n)
WaitGroup Donewg.Done()selesai_tunggu(wg)
WaitGroup Waitwg.Wait()tunggu_semua(wg)

Goroutine Dasar — luncurkan()

Gunakan luncurkan(fn, arg1, arg2...) untuk menjalankan fungsi secara bersamaan.

Contoh — Goroutine Sederhana
fungsi ucapkan(nama) {
    tampilkan("Halo dari goroutine:", nama)
}

// Jalankan 5 goroutine bersamaan
luncurkan(ucapkan, "Budi")
luncurkan(ucapkan, "Siti")
luncurkan(ucapkan, "Andi")
luncurkan(ucapkan, "Rini")
luncurkan(ucapkan, "Dani")

// Tunggu goroutine selesai (sederhana)
tidur(100)
tampilkan("Program selesai!")
Output (urutan bisa berbeda)
Halo dari goroutine: Siti
Halo dari goroutine: Budi
Halo dari goroutine: Andi
Halo dari goroutine: Dani
Halo dari goroutine: Rini
Program selesai!

Menunggu Goroutine — grup_tunggu()

Gunakan grup_tunggu() (WaitGroup) untuk menunggu semua goroutine selesai sebelum melanjutkan.

Contoh — WaitGroup
buat wg = grup_tunggu()

fungsi proses(id) {
    tampilkan("Mulai proses", id)
    tidur(200)  // simulasi pekerjaan
    tampilkan("Selesai proses", id)
    selesai_tunggu(wg)  // beri tahu WaitGroup bahwa goroutine ini selesai
}

// Tambah 3 goroutine ke counter
tambah_tunggu(wg, 3)

// Jalankan 3 goroutine bersamaan
luncurkan(proses, 1)
luncurkan(proses, 2)
luncurkan(proses, 3)

// Blokir sampai semua selesai
tunggu_semua(wg)
tampilkan("Semua proses selesai!")
Output
Mulai proses 1
Mulai proses 2
Mulai proses 3
Selesai proses 2
Selesai proses 1
Selesai proses 3
Semua proses selesai!

Mutex — Proteksi Data Bersama

Saat beberapa goroutine mengakses variabel yang sama, gunakan kunci_baru() (Mutex) untuk mencegah race condition.

Contoh — Mutex Counter
buat mu      = kunci_baru()
buat counter = 0
buat wg      = grup_tunggu()

fungsi tambah_counter() {
    // Lock sebelum akses data bersama
    kunci(mu)
    counter += 1
    buka_kunci(mu)  // selalu unlock setelah selesai
    selesai_tunggu(wg)
}

// Jalankan 1000 goroutine
tambah_tunggu(wg, 1000)
buat i = 0
selama i < 1000 {
    luncurkan(tambah_counter)
    i += 1
}

tunggu_semua(wg)
tampilkan("Counter akhir:", counter)  // harus 1000
Output
Counter akhir: 1000

Channel (Saluran) — Komunikasi Antar Goroutine

Channel adalah cara aman untuk berkomunikasi antar goroutine. Gunakan saluran_baru(n) untuk membuat channel dengan buffer ukuran n.

Contoh — Channel Dasar
// Channel tanpa buffer (blocking)
buat ch = saluran_baru(0)

// Goroutine pengirim
fungsi kirim_data(saluran) {
    buat i = 1
    selama i <= 5 {
        tampilkan("Mengirim:", i)
        kirim(saluran, i)
        i += 1
    }
    tutup_saluran(saluran)
}

luncurkan(kirim_data, ch)

// Penerima (di goroutine utama — blocking)
buat nilai = terima(ch)
selama nilai != nihil {
    tampilkan("Menerima:", nilai)
    nilai = terima(ch)
}
Output
Mengirim: 1
Menerima: 1
Mengirim: 2
Menerima: 2
...

Channel Berbufer

Contoh — Buffered Channel
// Channel dengan buffer 5 — bisa kirim 5 tanpa menunggu penerima
buat ch = saluran_baru(5)

kirim(ch, "pesanan-1")
kirim(ch, "pesanan-2")
kirim(ch, "pesanan-3")
tampilkan("3 pesanan masuk antrian")

// Proses satu per satu
buat p1 = terima(ch)
buat p2 = terima(ch)
buat p3 = terima(ch)
tampilkan("Diproses:", p1, p2, p3)

Pola: Worker Pool

Worker Pool adalah pola concurrency dimana sekelompok goroutine (worker) memproses antrian pekerjaan (jobs) secara paralel.

Contoh — Worker Pool
buat jobs    = saluran_baru(100)  // antrian pekerjaan
buat results = saluran_baru(100)  // hasil pekerjaan
buat wg      = grup_tunggu()

// Worker — ambil job dari channel, proses, kirim hasil
fungsi worker(id, jobs_ch, results_ch) {
    buat job = terima(jobs_ch)
    selama job != nihil {
        // Simulasi pekerjaan berat
        buat hasil = job * job  // kuadratkan angka
        tampilkan("Worker", id, "proses job", job, "→", hasil)
        kirim(results_ch, hasil)
        job = terima(jobs_ch)
    }
    selesai_tunggu(wg)
}

// Buat 3 worker
tambah_tunggu(wg, 3)
luncurkan(worker, 1, jobs, results)
luncurkan(worker, 2, jobs, results)
luncurkan(worker, 3, jobs, results)

// Kirim 9 pekerjaan
buat i = 1
selama i <= 9 {
    kirim(jobs, i)
    i += 1
}
tutup_saluran(jobs)

// Tunggu semua worker selesai
tunggu_semua(wg)
tutup_saluran(results)

tampilkan("Semua pekerjaan selesai!")

Goroutine untuk Background Task

Contoh — Background Job di Server
buat app = server_baru(8080)

// Background job: kirim reminder setiap 60 detik
fungsi kirim_reminder() {
    selama benar {
        tampilkan("[" + format_waktu(waktu_sekarang()) + "] Mengirim reminder email...")
        // ... logika kirim email di sini
        tidur(60000)  // tunggu 60 detik
    }
}

// Jalankan background job tanpa blokir server
luncurkan(kirim_reminder)

tambah_rute(app, "GET", "/", fungsi(req) {
    kembalikan ke_json({"status": "ok", "waktu": format_waktu(waktu_sekarang())})
})

tampilkan("Server berjalan, background job aktif!")
jalankan_server(app)
⚠️ Perhatian: Selalu gunakan kunci() dan buka_kunci() saat goroutine mengakses variabel yang dibagi bersama. Tanpa mutex, program bisa menghasilkan hasil yang tidak terduga akibat race condition.

Fungsi Lanjut — tidur()

FungsiParameterKeterangan
tidur(ms)milidetikJeda eksekusi N milidetik
tidur(1000)Jeda 1 detik
tidur(500)Jeda 0,5 detik
tidur(60000)Jeda 1 menit