← Back to Cecep Azhar
Cecep Azhar #golang#concurrency#goroutines#channels#parallel

Concurrency di Go: Goroutines dan Channels

Cecep Saeful Azhar • 16 Oct 2026
Concurrency di Go: Goroutines dan Channels
Panduan lengkap concurrency di Go. Goroutines, channels, sync patterns, dan common pitfalls.

Go's Killer Feature: Concurrency

Jika ada satu alasan kenapa saya memilih Go untuk backend SaaS, itu adalah concurrency model-nya. Goroutines yang ringan dan channels yang elegant membuat menulis concurrent code menjadi menyenangkan.

Saya pernah membangun service yang memproses 10K+ events per detik secara concurrent dengan mudah di Go.

Goroutines: Lebih Ringan dari yang Anda Pikir

Goroutine itu lightweight thread yang di-manage oleh Go runtime. Satu goroutine hanya butuh ~2KB stack dibandingkan ~1MB untuk OS thread. Anda bisa menjalankan ratusan ribu goroutines bersamaan.

package main

import (
    "fmt"
    "time"
)

func main() {
    go func() {
        fmt.Println("Hello from goroutine!")
    }()

    for i := 0; i < 5; i++ {
        go func(id int) {
            fmt.Printf("Worker %d started\n", id)
            time.Sleep(time.Second)
            fmt.Printf("Worker %d finished\n", id)
        }(i)
    }

    time.Sleep(2 * time.Second)
}

Channels: Communication Between Goroutines

Channels adalah cara goroutines berkomunikasi. Ingat filosofi Go: "Don't communicate by sharing memory; share memory by communicating."

func main() {
    ch := make(chan string)

    go func() {
        ch <- "data from worker"
    }()

    msg := <-ch
    fmt.Println(msg)

    buffered := make(chan int, 10)
    go func() {
        for i := 0; i < 10; i++ {
            buffered <- i
        }
        close(buffered)
    }()

    for val := range buffered {
        fmt.Println(val)
    }
}

Real-World Pattern: Fan-Out Fan-In

Ini pattern yang saya pakai untuk parallel processing di SaaS:

func processItems(items []Item) []Result {
    results := make(chan Result, len(items))

    for i := 0; i < 4; i++ {
        go func(workerID int) {
            for _, item := range items {
                result := process(item)
                results <- result
            }
        }(i)
    }

    var allResults []Result
    for i := 0; i < len(items); i++ {
        allResults = append(allResults, <-results)
    }
    return allResults
}

Common Pitfalls

Concurrency di Go itu powerful, tapi bukan magic. Gunakan sync.WaitGroup, sync.Mutex, dan context.Context untuk patterns yang lebih complex. Dan selalu: test with race detector enabled.

Context: Mengontrol Goroutine Lifecycle

Context adalah kunci untuk manage goroutine lifecycle di Go. Tanpa proper context handling, Anda akan mengalami goroutine leak yang memakan memory server:

func FetchData(ctx context.Context, urls []string) []string {
    results := make(chan string, len(urls))

    for _, url := range urls {
        go func(u string) {
            req, _ := http.NewRequestWithContext(ctx, "GET", u, nil)
            resp, err := http.DefaultClient.Do(req)
            if err != nil {
                return // Context cancelled, early exit
            }
            defer resp.Body.Close()
            body, _ := io.ReadAll(resp.Body)
            results <- string(body)
        }(url)
    }

    var data []string
    for i := 0; i < len(urls); i++ {
        select {
        case d := <-results:
            data = append(data, d)
        case <-ctx.Done():
            return data // Timeout or cancelled
        }
    }
    return data
}

Dengan context, semua goroutine akan otomatis berhenti ketika timeout atau request dibatalkan. Ini pattern yang harus selalu Anda pakai di production Go code.

sync.Pool: Mengurangi GC Pressure

Di high-throughput applications, object allocation yang berlebihan bisa bikin GC bekerja terlalu keras. Gunakan sync.Pool untuk reuse objects:

var bufPool = sync.Pool{
    New: func() interface{} {
        return new(bytes.Buffer)
    },
}

func ProcessRequest(data []byte) []byte {
    buf := bufPool.Get().(*bytes.Buffer)
    defer func() {
        buf.Reset()
        bufPool.Put(buf)
    }()
    buf.Write(data)
    return buf.Bytes()
}
← More articles from Cecep Azhar Central Content Hub Home →