Mutex

Mutex (Mutual Exclusion) adalah mekanisme sinkronisasi yang memastikan hanya satu goroutine yang dapat mengakses shared resource pada satu waktu.

Contoh Masalah

Bagaimana cara:

  1. Menghindari race condition
  2. Mengamankan shared resource
  3. 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

  1. Basic Mutex

    • Lock dan Unlock
    • Defer untuk Unlock
    • Protect shared data
  2. RWMutex

    • Multiple readers
    • Single writer
    • Better performance
  3. 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