Lompati ke konten utama
Modul 7 · Pelajaran 4 dari 534/50 dalam kursus~14 min
Pelajaran modul (4/5)

`sinkronisasi.Mutex` dan `sinkronisasi.WaitGroup`

Saluran adalah idiom utama Go, namun terkadang lebih tradisional primitif diperlukan. Paket sync menyediakan mutex, tunggu kelompok dan utilitas lain untuk mengoordinasikan keadaan bersama.

sync.Mutex: kunci eksklusif

Go
import "sync"

type Counter struct {
    mu    sync.Mutex
    count int
}

func (c *Counter) Inc() {
    c.mu.Lock()
    defer c.mu.Unlock()
    c.count++
}

func (c *Counter) Get() int {
    c.mu.Lock()
    defer c.mu.Unlock()
    return c.count
}

Hanya satu goroutine dalam satu waktu yang masuk antara Lock() dan Unlock(). Pola wajib: defer mu.Unlock() tepat setelah Lock().

sync.RWMutex: banyak pembaca, satu penulis

Untuk kondisi baca-berat:

Go
var mu sync.RWMutex

mu.RLock()              // read lock: più goroutine simultanee OK
v := data
mu.RUnlock()

mu.Lock()               // write lock: esclusivo
data = newValue
mu.Unlock()

Berguna hanya jika pembacaan benar-benar mendominasi penulisan; jika tidak, biasa saja sync.Mutex lebih sederhana dan seringkali lebih cepat berkat internal yang lebih rendah perselisihan.

sync.WaitGroup: tunggu N goroutine

Go
var wg sync.WaitGroup

for i := 0; i < 3; i++ {
    wg.Add(1)
    go func(id int) {
        defer wg.Done()
        work(id)
    }(i)
}
wg.Wait()    // blocca finché tutti i Done() sono arrivati

Alur kerja:

  1. wg.Add(N) menambah penghitung sebesar N (sebelum meluncurkan goroutine).
  2. Setiap goroutine memanggil wg.Done() setelah selesai (idealnya melalui defer).
  3. wg.Wait() blok hingga penghitung kembali ke 0.

sync.Once: inisialisasi malas yang aman untuk thread

Go
var (
    once   sync.Once
    config *Config
)

func GetConfig() *Config {
    once.Do(func() {
        config = loadConfig()
    })
    return config
}

Callback di once.Do berjalan tepat sekali, bahkan dalam kondisi bersamaan panggilan. Pola inisialisasi singleton/malas.

Mutex vs saluran: kapan menggunakan apa

Pedoman (juga dari FAQ resmi Go):

KasusAlat pilihan
Melewati kepemilikan datasaluran
Mendistribusikan pekerjaan (antrian pekerjaan)saluran
Mengkoordinasikan goroutine independensaluran
Melindungi bidang structMuteks
Tembolok/penghitungMutex atau atomik
Referensi tunggalsync.Once

"Saluran untuk diatur, mutex untuk data bersama" adalah aturan praktis yang baik.

Detektor balapan

Jalankan pengujian dengan -race untuk menemukan data race:

Bash
go test -race ./...
go run -race main.go

Detektor balapan menginstrumentasikan biner dan mencatat setiap hal yang tidak disinkronkan akses ke memori bersama. Ini adalah alat #1 untuk memvalidasi kode bersamaan sebelum produksi.

Cobalah

Latihan#go.m7.l4.e1
Upaya: 0Memuat…

Lindungi penambahan hitungan dengan mu.Lock() + defer mu.Unlock().

Memuat editor…
Tunjukkan petunjuk

tunda mu.Unlock() tepat setelah Lock() menjamin pelepasan bahkan saat panik.

Solusi tersedia setelah 3 upaya

Latihan#go.m7.l4.e2
Upaya: 0Memuat…

Luncurkan 3 goroutine dan tunggu semuanya menggunakan sync.WaitGroup (Tambahkan(3), Selesai() di setiap goroutine, Tunggu()).

Memuat editor…
Tunjukkan petunjuk

Tambahkan SEBELUM meluncurkan goroutine; Selesai DI DALAM goroutine dengan penundaan.

Solusi tersedia setelah 3 upaya

Quiz#go.m7.l4.e3
Siap

Pola apa yang direkomendasikan untuk menjamin mutex dilepaskan?

Go
mu.Lock()
// ?
Pilihan jawaban

Rekap

  • sync.Mutex melindungi negara bersama; selalu defer mu.Unlock().
  • Gunakan penerima penunjuk untuk menghindari penyalinan mutex.
  • sync.RWMutex untuk pola banyak pembaca/satu penulis (hanya jika membantu).
  • sync.WaitGroup: Add sebelum go, Done dengan defer, Wait menunggu.
  • sync.Once: inisialisasi lambat yang aman untuk thread.
  • "Saluran untuk diatur, mutex untuk data bersama".
  • go test -race: alat #1 untuk mengenali data race.