Race Condition & Mutual Exclusion di Go
Lu pasti pernah ngalamin kode Go lo bertingkah aneh, muncul bug yang kadang-kadang aja nongol? Bisa jadi lo lagi berhadapan sama race condition. Yuk kita kupas bareng: apa sih itu, kenapa bisa kejadian, dan gimana cara ngatasinnya biar kode lo tetap aman dan nggak ngawur.
Race Condition Itu Apa, Sih? 🤔
Race condition itu kejadian di mana dua atau lebih goroutine (kayak thread ringan) ngakses dan ubah data yang sama di waktu yang barengan. Karena gak ada yang ngatur siapa duluan yang ngakses, hasil akhirnya bisa kacau, kadang data jadi salah, atau yang lebih parah program bisa crash. Bayangin aja dua orang lagi ngedit satu sel di Google Sheet barengan and bisa jadi conflict.
Nih Contohnya:
Misal lo punya counter yang diubah sama banyak goroutine:
func increment(wg *sync.WaitGroup) {
defer wg.Done()
for i := 0; i < 10000; i++ {
counter++
}
}
func TestRaceCondition(t *testing.T) {
var wg sync.WaitGroup
wg.Add(10)
for i := 0; i < 10; i++ {
go increment(&wg)
}
wg.Wait()
log.Println(counter)
}
Ekspektasinya sih counter
bakal jadi 100000, tapi kenyataannya bisa beda. Nah, itu karena race condition!
Output:
go test -v -run=TestRaceCondition
=== RUN TestRaceCondition
2025/06/18 20:52:21 40579
--- PASS: TestRaceCondition (0.00s)
PASS
ok race-condition 0.767s
Gimana Tau Kalo Kena Race Condition? 🕵️
Tenang, Go punya tools buat bantuin lo. Tinggal jalanin pake flag -race
:
go run -race file.go
Output:
go test -v -race -run=TestRaceCondition
=== RUN TestRaceCondition
==================
WARNING: DATA RACE
Read at 0x0001008c5530 by goroutine 14:
race-condition.increment()
Kalau ada race condition, Go bakal kasih tau lokasi masalahnya. Ngebantu banget buat debug.
Solusi
1. Pakai Mutual Exclusion (Mutex)
Mutex itu semacam satpam yang cuma ngasih satu goroutine buat masuk dan ubah data di waktu yang sama.
func TestMutex(t *testing.T) {
var counter int
var mu sync.Mutex
var wg sync.WaitGroup
for range 10000 {
wg.Add(1)
go func() {
defer wg.Done()
mu.Lock()
counter++
mu.Unlock()
}()
}
wg.Wait()
log.Println("Counter:", counter)
}
go test -v -run=TestMutex
=== RUN TestMutex
=== RUN TestMutex
2025/06/18 21:54:25 Counter: 10000
--- PASS: TestMutex (0.00s)
--- PASS: TestMutex (0.00s)
PASS
ok race-condition 0.196s
Dengan mutex, counter
bakal stabil di 10000. Jadi nggak saling tabrak antar goroutine.
2. Pakai Channel
Channel itu bikin goroutine bisa komunikasi dengan aman.
counter := 0
ch := make(chan bool)
for i := 0; i < 1000; i++ {
go func() {
ch <- true
}()
go func() {
counter++
<-ch
}()
}
Tapi jujur buat urusan counter doang, mutex lebih simpel. Channel cocok kalau lo mau ngatur alur yang lebih kompleks antar goroutine.
3. Atomic Operation
Paket sync/atomic
di Go bisa dipakai buat operasi thread-safe tanpa perlu mutex.
package main
import (
"log"
"sync/atomic"
"testing"
"time"
)
var counter int64 = 0
func TestAtomic(t *testing.T) {
for i := 0; i < 10000; i++ {
go func() {
atomic.AddInt64(&counter, 1)
}()
}
time.Sleep(time.Second)
log.Println("Counter:", counter)
}
go test -v -race -run=TestAtomic
=== RUN TestAtomic
2025/06/18 22:00:13 Counter: 10000
--- PASS: TestAtomic (1.03s)
PASS
ok race-condition 2.400s
4. Read-Write Mutex
var rwmu sync.RWMutex
// Banyak yang bisa baca
rwmu.RLock()
value := counter
rwmu.RUnlock()
// Cuma satu yang bisa nulis
rwmu.Lock()
counter++
rwmu.Unlock()
RWMutex cocok kalau kebanyakan proses cuma baca data, karena gak perlu nunggu kayak mutex biasa.
Pro Tips 🎯
- Selalu tes pake
-race
biar bisa deteksi masalah lebih awal:
go test -race ./...
- Makin dikit data yang dishare antar goroutine, makin kecil kemungkinan kena race condition.
- Setelah
mu.Lock()
, langsung ajadefer mu.Unlock()
biar gak lupa buka kunci—bisa-bisa program lo nge-freeze (deadlock).
TL;DR
- Race condition = bug gak ketebak gara-gara data diakses barengan tanpa kontrol.
- Kapan pun lo pakai goroutine dan share data, wajib banget mikirin race condition.
- Pakai
-race
buat cek dan deteksi. - Solusinya: mutex, channel, atau atomic.
- Kalau bisa, pakai channel atau pattern bawaan Go lain daripada harus ngelock manual.
Kalau lu udah paham konsep race condition dan gimana cara debug + solve sihh gua akuin disitu lu nyampe yee.