Lompat ke konten Lompat ke sidebar Lompat ke footer

Golang Concurrency #3

 

Mekanisme sync.WaitGroup di Golang

sync.WaitGroup adalah mekanisme sinkronisasi di Golang yang digunakan untuk menunggu sekelompok goroutine selesai dieksekusi sebelum program utama melanjutkan eksekusi.


1. Konsep Dasar sync.WaitGroup

Sebuah WaitGroup bekerja dengan tiga metode utama:

  1. wg.Add(n) → Menambahkan jumlah goroutine yang akan ditunggu.
  2. wg.Done() → Menandakan bahwa satu goroutine telah selesai dieksekusi.
  3. wg.Wait() → Menunggu sampai semua goroutine selesai.

Catatan:

  • wg.Add(n) harus dipanggil di goroutine utama sebelum goroutine dijalankan.
  • wg.Done() harus dipanggil di dalam goroutine untuk mengurangi counter WaitGroup.
  • wg.Wait() harus dipanggil di goroutine utama untuk menunggu semua goroutine selesai.

2. Contoh Sederhana WaitGroup

package main import ( "fmt" "sync" "time" ) func worker(id int, wg *sync.WaitGroup) { // Memberitahu bahwa goroutine ini akan selesai nanti defer wg.Done() fmt.Printf("Worker %d mulai bekerja\n", id) time.Sleep(2 * time.Second) // Simulasi pekerjaan fmt.Printf("Worker %d selesai\n", id) } func main() { var wg sync.WaitGroup // Menjalankan 3 worker secara concurrent for i := 1; i <= 3; i++ { wg.Add(1) // Menambah counter sebelum menjalankan goroutine go worker(i, &wg) } wg.Wait() // Menunggu semua goroutine selesai fmt.Println("Semua pekerjaan selesai") }

3. Cara Kerja WaitGroup Secara Mendetail

  1. wg.Add(1) dipanggil sebelum setiap goroutine dijalankan untuk menambah counter WaitGroup.
  2. go worker(i, &wg) menjalankan goroutine worker.
  3. wg.Done() dipanggil dalam worker untuk mengurangi counter WaitGroup saat worker selesai.
  4. wg.Wait() akan memblokir eksekusi di main sampai semua worker selesai.

Ilustrasi Proses WaitGroup

Misalkan ada 3 goroutine, WaitGroup bekerja seperti ini:

EventCounter WaitGroup
wg.Add(1) worker 11
wg.Add(1) worker 22
wg.Add(1) worker 33
Worker 1 selesai (wg.Done())2
Worker 2 selesai (wg.Done())1
Worker 3 selesai (wg.Done())0
wg.Wait() selesai, program berlanjut0

Saat counter menjadi 0, wg.Wait() berhenti memblokir dan program melanjutkan eksekusi.


4. Kesalahan Umum dalam Penggunaan WaitGroup

1. Lupa Memanggil wg.Done()

Jika goroutine tidak memanggil wg.Done(), maka wg.Wait() akan menunggu selamanya.

func worker(wg *sync.WaitGroup) { // Lupa memanggil wg.Done() fmt.Println("Worker mulai bekerja") time.Sleep(2 * time.Second) fmt.Println("Worker selesai") } func main() { var wg sync.WaitGroup wg.Add(1) // Menambahkan counter go worker(&wg) wg.Wait() // Akan macet di sini karena counter tidak pernah berkurang! fmt.Println("Program selesai") }

Solusi: Gunakan defer wg.Done()

func worker(wg *sync.WaitGroup) { defer wg.Done() // Memastikan wg.Done() selalu dipanggil fmt.Println("Worker mulai bekerja") time.Sleep(2 * time.Second) fmt.Println("Worker selesai") }

2. Memanggil wg.Done() Lebih dari wg.Add()

Jika wg.Done() dipanggil lebih banyak dari wg.Add(), maka program akan panic karena counter menjadi negatif.

package main import ( "fmt" "sync" ) func main() { var wg sync.WaitGroup wg.Add(1) go func() { wg.Done() wg.Done() // ❌ Ini akan menyebabkan panic }() wg.Wait() fmt.Println("Program selesai") }

Solusi: Pastikan jumlah wg.Done() tidak lebih banyak dari wg.Add().


3. Memanggil wg.Add() di dalam Goroutine

package main import ( "fmt" "sync" ) func main() { var wg sync.WaitGroup go func() { wg.Add(1) // ❌ Tidak boleh dipanggil dalam goroutine yang berjalan fmt.Println("Goroutine berjalan") wg.Done() }() wg.Wait() }

Solusi: Panggil wg.Add() di main thread sebelum goroutine berjalan.


5. Kapan Menggunakan sync.WaitGroup?

Gunakan WaitGroup jika: ✅ Ada beberapa goroutine yang harus diselesaikan sebelum program melanjutkan.
✅ Tidak ingin menggunakan time.Sleep() untuk menunggu goroutine selesai.
✅ Butuh sinkronisasi sederhana tanpa harus menggunakan channel.

Jika ada pertanyaan atau ingin contoh lebih kompleks, silakan tanyakan!