Mutex
Mutex (Mutual Exclusion) adalah mekanisme sinkronisasi yang memastikan hanya satu goroutine yang dapat mengakses shared resource pada satu waktu.
Contoh Masalah
Bagaimana cara:
- Menghindari race condition
- Mengamankan shared resource
- Sinkronisasi akses concurrent
Penyelesaian
package main
import (
"fmt"
"sync"
"time"
)
// 1. Counter dengan race condition
type Counter struct {
value int
}
// 2. Safe counter dengan mutex
type SafeCounter struct {
mu sync.Mutex
value int
}
func (c *SafeCounter) Increment() {
c.mu.Lock()
defer c.mu.Unlock()
c.value++
}
func (c *SafeCounter) Value() int {
c.mu.Lock()
defer c.mu.Unlock()
return c.value
}
// 3. RWMutex example
type ReadWriteMap struct {
sync.RWMutex
data map[string]int
}
func NewReadWriteMap() *ReadWriteMap {
return &ReadWriteMap{
data: make(map[string]int),
}
}
func (m *ReadWriteMap) Set(key string, value int) {
m.Lock()
defer m.Unlock()
m.data[key] = value
}
func (m *ReadWriteMap) Get(key string) (int, bool) {
m.RLock()
defer m.RUnlock()
val, ok := m.data[key]
return val, ok
}
// 4. Bank account example
type BankAccount struct {
sync.Mutex
balance int
}
func (b *BankAccount) Deposit(amount int) {
b.Lock()
defer b.Unlock()
b.balance += amount
}
func (b *BankAccount) Withdraw(amount int) bool {
b.Lock()
defer b.Unlock()
if b.balance >= amount {
b.balance -= amount
return true
}
return false
}
func (b *BankAccount) Balance() int {
b.Lock()
defer b.Unlock()
return b.balance
}
func main() {
// Contoh 1: Race condition demonstration
fmt.Println("Race Condition Example:")
counter := Counter{}
var wg sync.WaitGroup
for i := 0; i < 1000; i++ {
wg.Add(1)
go func() {
defer wg.Done()
counter.value++ // Race condition!
}()
}
wg.Wait()
fmt.Printf("Unsafe counter final value: %d\n", counter.value)
// Contoh 2: Safe counter dengan mutex
fmt.Printf("\nSafe Counter Example:\n")
safeCounter := SafeCounter{}
for i := 0; i < 1000; i++ {
wg.Add(1)
go func() {
defer wg.Done()
safeCounter.Increment()
}()
}
wg.Wait()
fmt.Printf("Safe counter final value: %d\n", safeCounter.Value())
// Contoh 3: RWMutex usage
fmt.Printf("\nRWMutex Example:\n")
m := NewReadWriteMap()
// Writers
for i := 0; i < 5; i++ {
wg.Add(1)
go func(n int) {
defer wg.Done()
m.Set(fmt.Sprintf("key%d", n), n)
fmt.Printf("Written: key%d = %d\n", n, n)
}(i)
}
// Readers
for i := 0; i < 10; i++ {
wg.Add(1)
go func(n int) {
defer wg.Done()
key := fmt.Sprintf("key%d", n%5)
if val, ok := m.Get(key); ok {
fmt.Printf("Read: %s = %d\n", key, val)
}
}(i)
}
wg.Wait()
// Contoh 4: Bank account
fmt.Printf("\nBank Account Example:\n")
account := &BankAccount{}
// Concurrent deposits
for i := 0; i < 5; i++ {
wg.Add(1)
go func() {
defer wg.Done()
account.Deposit(100)
fmt.Printf("Deposited 100, balance: %d\n", account.Balance())
}()
}
// Concurrent withdrawals
for i := 0; i < 3; i++ {
wg.Add(1)
go func() {
defer wg.Done()
if account.Withdraw(150) {
fmt.Printf("Withdrew 150, balance: %d\n", account.Balance())
} else {
fmt.Printf("Withdrawal failed, balance: %d\n", account.Balance())
}
}()
}
wg.Wait()
fmt.Printf("Final balance: %d\n", account.Balance())
// Contoh 5: Mutex timeout pattern
fmt.Printf("\nMutex Timeout Example:\n")
var mu sync.Mutex
done := make(chan bool)
go func() {
mu.Lock()
time.Sleep(time.Second) // Simulate long operation
mu.Unlock()
done <- true
}()
// Try to acquire lock with timeout
timeout := time.After(500 * time.Millisecond)
select {
case <-done:
fmt.Println("Operation completed")
case <-timeout:
fmt.Println("Operation timeout")
}
}
Penjelasan Kode
Basic Mutex
- Lock dan Unlock
- Defer untuk Unlock
- Protect shared data
RWMutex
- Multiple readers
- Single writer
- Better performance
Patterns
- Resource protection
- Concurrent access
- Timeout handling
Output
Race Condition Example:
Unsafe counter final value: 952
Safe Counter Example:
Safe counter final value: 1000
RWMutex Example:
Written: key0 = 0
Written: key1 = 1
Read: key1 = 1
Read: key2 = 2
Written: key2 = 2
Read: key0 = 0
Written: key3 = 3
Read: key3 = 3
Written: key4 = 4
Read: key4 = 4
Read: key0 = 0
Read: key1 = 1
Read: key2 = 2
Read: key3 = 3
Bank Account Example:
Deposited 100, balance: 100
Deposited 100, balance: 200
Deposited 100, balance: 300
Deposited 100, balance: 400
Deposited 100, balance: 500
Withdrew 150, balance: 350
Withdrew 150, balance: 200
Withdrew 150, balance: 50
Final balance: 50
Mutex Timeout Example:
Operation timeout
Tips
- Selalu unlock mutex
- Gunakan defer untuk unlock
- Minimalisir critical section
- RWMutex untuk read-heavy workload
- Hindari nested locks