Concurrency Lanjutan di Golang: Select, Context, dan Pipeline Pattern

Setelah memahami dasar concurrency dengan goroutines dan channels, langkah selanjutnya adalah mempelajari fitur lanjutan seperti select, context, dan pipeline pattern. Fitur ini akan membantu kita menulis program yang lebih robust, scalable, dan mudah dikelola.


Select Statement

select digunakan untuk menunggu banyak channel secara bersamaan. Mirip seperti switch, tapi khusus untuk operasi channel.

Contoh 1: Select untuk Multiple Channels

package main

import (
    "fmt"
    "time"
)

func main() {
    ch1 := make(chan string)
    ch2 := make(chan string)

    go func() {
        time.Sleep(2 * time.Second)
        ch1 <- "Data dari ch1"
    }()

    go func() {
        time.Sleep(1 * time.Second)
        ch2 <- "Data dari ch2"
    }()

    for i := 0; i < 2; i++ {
        select {
        case msg1 := <-ch1:
            fmt.Println("Received:", msg1)
        case msg2 := <-ch2:
            fmt.Println("Received:", msg2)
        }
    }
}

Output:

Received: Data dari ch2
Received: Data dari ch1

select memungkinkan kita memilih channel mana yang lebih dulu siap.


Context untuk Manajemen Goroutine

context digunakan untuk mengatur lifecycle goroutine: cancel, timeout, dan deadline. Sangat berguna agar goroutine tidak jalan selamanya (goroutine leak).

Contoh 2: Context dengan Timeout

package main

import (
    "context"
    "fmt"
    "time"
)

func doWork(ctx context.Context) {
    for {
        select {
        case <-ctx.Done():
            fmt.Println("Pekerjaan dihentikan:", ctx.Err())
            return
        default:
            fmt.Println("Bekerja...")
            time.Sleep(500 * time.Millisecond)
        }
    }
}

func main() {
    ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
    defer cancel()

    go doWork(ctx)

    time.Sleep(3 * time.Second)
    fmt.Println("Selesai main program")
}

Output:

Bekerja...
Bekerja...
Bekerja...
Pekerjaan dihentikan: context deadline exceeded
Selesai main program

Pipeline Pattern

Pipeline adalah cara menyusun alur data antar goroutine. Setiap stage menerima data dari channel, memprosesnya, lalu mengirim ke channel berikutnya.

Contoh 3: Pipeline Sederhana

package main

import "fmt"

func generate(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() {
    nums := generate(1, 2, 3, 4, 5)
    sq := square(nums)

    for result := range sq {
        fmt.Println(result)
    }
}

Output:

1
4
9
16
25

Pipeline sangat berguna untuk memproses data bertahap, mirip assembly line.


Kesimpulan

  • Select: menunggu banyak channel sekaligus, memilih yang siap lebih dulu.
  • Context: mengontrol lifecycle goroutine dengan cancel, timeout, atau deadline.
  • Pipeline: mengalirkan data antar goroutine secara berurutan.

Fitur lanjutan ini menjadikan concurrency di Golang lebih fleksibel, aman, dan mudah dikelola pada aplikasi berskala besar.