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:
wg.Add(n)→ Menambahkan jumlah goroutine yang akan ditunggu.wg.Done()→ Menandakan bahwa satu goroutine telah selesai dieksekusi.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 counterWaitGroup.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
wg.Add(1)dipanggil sebelum setiap goroutine dijalankan untuk menambah counterWaitGroup.go worker(i, &wg)menjalankan goroutine worker.wg.Done()dipanggil dalamworkeruntuk mengurangi counterWaitGroupsaat worker selesai.wg.Wait()akan memblokir eksekusi dimainsampai semua worker selesai.
Ilustrasi Proses WaitGroup
Misalkan ada 3 goroutine, WaitGroup bekerja seperti ini:
| Event | Counter WaitGroup |
|---|---|
wg.Add(1) worker 1 | 1 |
wg.Add(1) worker 2 | 2 |
wg.Add(1) worker 3 | 3 |
Worker 1 selesai (wg.Done()) | 2 |
Worker 2 selesai (wg.Done()) | 1 |
Worker 3 selesai (wg.Done()) | 0 |
wg.Wait() selesai, program berlanjut | 0 |
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!