Goroutines

Goroutine adalah unit eksekusi ringan yang berjalan secara concurrent di Go. Goroutine memungkinkan kita menjalankan fungsi secara asynchronous.

Contoh Masalah

Bagaimana cara:

  1. Menjalankan kode secara concurrent
  2. Mengelola multiple tasks
  3. Meningkatkan performa aplikasi

Penyelesaian

package main

import (
    "fmt"
    "math/rand"
    "sync"
    "time"
)

// 1. Fungsi yang akan dijalankan dalam goroutine
func worker(id int, wg *sync.WaitGroup) {
    defer wg.Done() // Menandai goroutine selesai

    fmt.Printf("Worker %d starting\n", id)
    time.Sleep(time.Duration(rand.Intn(1000)) * time.Millisecond)
    fmt.Printf("Worker %d done\n", id)
}

// 2. Fungsi dengan channel
func generator(done chan bool) {
    for i := 0; i < 5; i++ {
        time.Sleep(time.Duration(rand.Intn(500)) * time.Millisecond)
        fmt.Printf("Generated value: %d\n", i)
    }
    done <- true
}

// 3. Worker pool pattern
func workerPool(id int, jobs <-chan int, results chan<- int) {
    for j := range jobs {
        fmt.Printf("worker %d started job %d\n", id, j)
        time.Sleep(time.Duration(rand.Intn(500)) * time.Millisecond)
        fmt.Printf("worker %d finished job %d\n", id, j)
        results <- j * 2
    }
}

// 4. Pipeline pattern
func generator2(nums ...int) <-chan int {
    out := make(chan int)
    go func() {
        for _, n := range nums {
            out <- n
        }
        close(out)
    }()
    return out
}

func square(in <-chan int) <-chan int {
    out := make(chan int)
    go func() {
        for n := range in {
            out <- n * n
        }
        close(out)
    }()
    return out
}

func main() {
    // Seed random number generator
    rand.Seed(time.Now().UnixNano())

    // Contoh 1: Basic goroutines dengan WaitGroup
    fmt.Println("Starting workers...")
    var wg sync.WaitGroup
    
    for i := 1; i <= 3; i++ {
        wg.Add(1)
        go worker(i, &wg)
    }
    
    wg.Wait()
    fmt.Println("All workers done")

    // Contoh 2: Goroutine dengan channel
    fmt.Printf("\nStarting generator...\n")
    done := make(chan bool)
    go generator(done)
    <-done
    fmt.Println("Generator done")

    // Contoh 3: Worker pool
    fmt.Printf("\nStarting worker pool...\n")
    const numJobs = 5
    jobs := make(chan int, numJobs)
    results := make(chan int, numJobs)

    // Start workers
    for w := 1; w <= 3; w++ {
        go workerPool(w, jobs, results)
    }

    // Send jobs
    for j := 1; j <= numJobs; j++ {
        jobs <- j
    }
    close(jobs)

    // Collect results
    for a := 1; a <= numJobs; a++ {
        <-results
    }

    // Contoh 4: Pipeline
    fmt.Printf("\nStarting pipeline...\n")
    // Generate numbers, square them, and print
    for n := range square(generator2(2, 3, 4, 5, 6)) {
        fmt.Printf("Pipeline result: %d\n", n)
    }

    // Contoh 5: Race condition prevention
    fmt.Printf("\nDemonstrating mutex...\n")
    counter := 0
    var mutex sync.Mutex

    var wg2 sync.WaitGroup
    for i := 0; i < 1000; i++ {
        wg2.Add(1)
        go func() {
            defer wg2.Done()
            mutex.Lock()
            counter++
            mutex.Unlock()
        }()
    }
    
    wg2.Wait()
    fmt.Printf("Final counter value: %d\n", counter)
}

Penjelasan Kode

  1. Basic Goroutine

    • Keyword go
    • Concurrent execution
    • Non-blocking
  2. Synchronization

    • WaitGroup
    • Mutex
    • Channels
  3. Patterns

    • Worker pool
    • Pipeline
    • Fan-out/fan-in

Output

Starting workers...
Worker 1 starting
Worker 2 starting
Worker 3 starting
Worker 2 done
Worker 1 done
Worker 3 done
All workers done

Starting generator...
Generated value: 0
Generated value: 1
Generated value: 2
Generated value: 3
Generated value: 4
Generator done

Starting worker pool...
worker 1 started job 1
worker 2 started job 2
worker 3 started job 3
worker 1 finished job 1
worker 1 started job 4
worker 2 finished job 2
worker 2 started job 5
worker 3 finished job 3
worker 1 finished job 4
worker 2 finished job 5

Starting pipeline...
Pipeline result: 4
Pipeline result: 9
Pipeline result: 16
Pipeline result: 25
Pipeline result: 36

Demonstrating mutex...
Final counter value: 1000

Tips

  • Gunakan WaitGroup untuk sinkronisasi
  • Hindari race condition dengan mutex
  • Gunakan channel untuk komunikasi
  • Perhatikan memory usage
  • Test dengan race detector