👨‍💻
Sammi
  • Hello
  • About Me
    • Links
    • My Daily Uses
  • PostgreSQL → Partitioning
  • Belajar System Programming Menggunakan Go
    • Mengingat Kembali Tentang Concurrency dan Parallelism
  • Memory Management
  • Explore
    • Testing 1: Load and performance testing
    • Data Structure 1: Bloom Filter
    • System Design 1: Back of The Envelope Estimation
    • System Design 2: A Framework For System Design Interviews
    • System Design 3: Design a URL Shortener
    • Belajar RabbitMQ
  • Belajar Kubernetes
  • Notes
    • Permasalahan Penggunaan JWT dan Solusinya dengan PASETO
    • First Principle Thinking
    • The Over-Engineering Pendulum
    • Data-Oriented Programming
  • CAP Theorem
  • Go Series: Safer Enum
  • Go Series: Different types of for loops in Golang?
  • Go Series: Mutex & RWMutex
  • Setup VM Production Ready Best Practice
  • BEHAVIOUR QUESTION
  • Invert, always invert
  • Mengapa Tidak Menggunakan Auto-Increment ID?
  • I Prefix dan Impl Suffix
  • ACID
  • MVCC Di Postgres
  • Implicit Interface di Go
  • Transaction di Postgres
  • Kriteria Kolom yang Cocok Dijadikan Index
  • Misc
    • Go Project
    • Talks
    • Medium Articles
  • PostgreSQL
    • Introduction
  • English
    • Vocab
Powered by GitBook
On this page
  • Mengapa Membutuhkan Mutex di Golang?
  • Kapan Menggunakan RWMutex?
  • Kesimpulan

Go Series: Mutex & RWMutex

Dalam pengembangan aplikasi dengan Go, masalah data race sering kali muncul ketika beberapa goroutine mengakses data bersama secara bersamaan. Data race terjadi saat dua atau lebih goroutine mengakses lokasi memori yang sama, dengan setidaknya salah satunya melakukan operasi tulis, sehingga menyebabkan data tidak konsisten dan bug yang sulit dilacak.

Dengan semakin umumnya prosesor multi-core pada berbagai perangkat, dari server hingga ponsel, penguasaan alat sinkronisasi seperti Mutex dan RWMutex menjadi penting untuk memastikan aplikasi Go berjalan dengan andal. Artikel ini menjelaskan cara kerja kedua mekanisme tersebut dalam mencegah data race pada aplikasi konkuren.

Mengapa Membutuhkan Mutex di Golang?

Data race terjadi ketika beberapa goroutine mengakses dan mengubah data bersama tanpa koordinasi yang memadai. Akibatnya, nilai data bisa menjadi tidak konsisten, terutama dalam sistem kritis seperti aplikasi perbankan atau pembayaran, yang dapat menyebabkan kerugian signifikan.

Go menyediakan Mutex (Mutual Exclusion) melalui paket sync untuk mengatasi masalah ini. Mutex memastikan hanya satu goroutine yang dapat mengakses data tertentu pada satu waktu, mencegah akses konkuren yang tidak diinginkan.

Berikut adalah contoh kode yang rentan terhadap data race:

type Account struct {
    balance int
    Name    string
}

func (a *Account) Withdraw(amount int) {
    a.balance -= amount
}

func (a *Account) Deposit(amount int) {
    a.balance += amount
}

func (a *Account) GetBalance() int {
    return a.balance
}

Jika beberapa goroutine memanggil metode Deposit atau Withdraw secara bersamaan, seperti pada kode berikut, data race akan terjadi:

var account Account
account.Name = "Akun Uji"

for i := 0; i < 20; i++ {
    wg.Add(1)
    go account.Deposit(100)
}

for i := 0; i < 10; i++ {
    wg.Add(1)
    go account.Withdraw(100)
}

wg.Wait()
fmt.Printf("Saldo: %d\n", account.GetBalance())

Eksekusi kode di atas dapat menghasilkan saldo yang berbeda-beda setiap kali dijalankan karena akses konkuren yang tidak terkoordinasi. Untuk mengatasinya, kita dapat menggunakan sync.Mutex dengan menambahkannya sebagai field pada struktur Account:

type Account struct {
    balance int
    Name    string
    lock    sync.Mutex
}

func (a *Account) Withdraw(amount int, wg *sync.WaitGroup) {
    defer wg.Done()
    a.lock.Lock()
    a.balance -= amount
    a.lock.Unlock()
}

func (a *Account) Deposit(amount int, wg *sync.WaitGroup) {
    defer wg.Done()
    a.lock.Lock()
    a.balance += amount
    a.lock.Unlock()
}

func (a *Account) GetBalance() int {
    a.lock.Lock()
    defer a.lock.Unlock()
    return a.balance
}

Kode kliennya menjadi:

var account Account
var wg sync.WaitGroup

account.Name = "Akun Uji"

for i := 0; i < 20; i++ {
    wg.Add(1)
    go account.Deposit(100, &wg)
}

for i := 0; i < 10; i++ {
    wg.Add(1)
    go account.Withdraw(100, &wg)
}

wg.Wait()
fmt.Printf("Saldo: %d\n", account.GetBalance())

Dengan Mutex, akses ke balance dikunci selama operasi tulis atau baca, sehingga hasilnya konsisten.

Kapan Menggunakan RWMutex?

Meskipun Mutex efektif, penguncian untuk operasi baca dapat mengurangi efisiensi, terutama jika banyak goroutine hanya perlu membaca data tanpa mengubahnya. Untuk kasus ini, Go menyediakan RWMutex (Read-Write Mutex), yang mendukung penguncian baca bersama (shared read lock) dan penguncian tulis eksklusif (exclusive write lock).

Dengan RWMutex, beberapa goroutine dapat membaca data secara bersamaan menggunakan RLock dan RUnlock, sementara operasi tulis tetap menggunakan Lock dan Unlock. Berikut adalah implementasinya:

type Account struct {
    balance int
    Name    string
    lock    sync.RWMutex
}

func (a *Account) Withdraw(amount int, wg *sync.WaitGroup) {
    defer wg.Done()
    a.lock.Lock()
    a.balance -= amount
    a.lock.Unlock()
}

func (a *Account) Deposit(amount int, wg *sync.WaitGroup) {
    defer wg.Done()
    a.lock.Lock()
    a.balance += amount
    a.lock.Unlock()
}

func (a *Account) GetBalance() int {
    a.lock.RLock()
    defer a.lock.RUnlock()
    return a.balance
}

Dengan RWMutex, metode GetBalance hanya menggunakan penguncian baca, memungkinkan banyak goroutine mengakses balance secara bersamaan tanpa memblokir satu sama lain, selama tidak ada operasi tulis yang sedang berlangsung.

Kesimpulan

Data race adalah masalah serius dalam pemrograman konkuren yang dapat menyebabkan bug yang sulit dideteksi. Menggunakan sync.Mutex adalah cara sederhana untuk melindungi data kritis dengan memastikan akses eksklusif. Namun, jika aplikasi Anda melibatkan banyak operasi baca dengan sedikit operasi tulis, sync.RWMutex dapat meningkatkan efisiensi dengan memungkinkan akses baca bersamaan.

Penting untuk memilih alat sinkronisasi yang sesuai dengan kebutuhan aplikasi. Jika jumlah pembaca konkuren sangat tinggi, pertimbangkan pendekatan lain seperti kanal atau pola copy-on-write untuk mengoptimalkan kinerja. Dengan penggunaan Mutex dan RWMutex yang tepat, Anda dapat membangun aplikasi Go yang aman dari data race dan berjalan dengan stabil.

PreviousGo Series: Different types of for loops in Golang?NextSetup VM Production Ready Best Practice

Last updated 16 days ago