5 Concurrency Patterns di Golang
3 min readJul 28, 2024
Concurrency patterns sangat penting dalam pengembangan aplikasi yang membutuhkan performa tinggi dan efisiensi. Berikut beberapa concurrency patterns yang umum digunakan di Golang
1. Worker Pool
- Deskripsi: Membuat sejumlah worker goroutine yang mengambil setiap pekerjaan dari sebuah kanal.
- Kegunaan: Cocok untuk membatasi jumlah goroutine yang berjalan secara bersamaan saat memproses pekerjaan yang banyak.
Example Code:
package main
import (
"fmt"
"sync"
)
func worker(id int, jobs <-chan int, results chan<- int) {
for j := range jobs {
fmt.Println("worker", id, "started job", j)
results <- j * 2
fmt.Println("worker", id, "finished job", j)
}
}
func main() {
const numJobs = 5
jobs := make(chan int, numJobs)
results := make(chan int, numJobs)
for w := 1; w <= 3; w++ {
go worker(w, jobs, results)
}
for j := 1; j <= numJobs; j++ {
jobs <- j
}
close(jobs)
for a := 1; a <= numJobs; a++ {
<-results
}
}
2. Pipeline
- Deskripsi: Memproses data melalui serangkaian tahap yang dijalankan secara concurrent.
- Kegunaan: Berguna untuk memproses data dalam beberapa tahap, seperti validasi, transformasi, dan penyimpanan.
Example Code:
package main
import "fmt"
func gen(nums ...int) <-chan int {
out := make(chan int)
go func() {
for _, n := range nums {
out <- n
}
close(out)
}()
return out
}
func sq(in <-chan int) <-chan int {
out := make(chan int)
go func() {
for n := range in {
out <- n * n
}
close(out)
}()
return out
}
func main() {
c := gen(2, 3, 4)
out := sq(c)
for n := range out {
fmt.Println(n)
}
}
3. Fan-out, Fan-in
- Deskripsi: Fan-out adalah pola di mana satu goroutine mendistribusikan job ke beberapa goroutine lainnya. Fan-in mengumpulkan hasil dari beberapa goroutine ke satu goroutine.
- Kegunaan: Berguna untuk memanfaatkan banyak CPU core dengan membagi pekerjaan besar menjadi pekerjaan kecil.
Example Code:
package main
import (
"fmt"
"sync"
)
func main() {
in := gen(2, 3, 4, 5)
c1 := sq(in)
c2 := sq(in)
for n := range merge(c1, c2) {
fmt.Println(n)
}
}
func gen(nums ...int) <-chan int {
out := make(chan int)
go func() {
for _, n := range nums {
out <- n
}
close(out)
}()
return out
}
func sq(in <-chan int) <-chan int {
out := make(chan int)
go func() {
for n := range in {
out <- n * n
}
close(out)
}()
return out
}
func merge(cs ...<-chan int) <-chan int {
var wg sync.WaitGroup
out := make(chan int)
output := func(c <-chan int) {
for n := range c {
out <- n
}
wg.Done()
}
wg.Add(len(cs))
for _, c := range cs {
go output(c)
}
go func() {
wg.Wait()
close(out)
}()
return out
}
4. Select Statement
- Deskripsi: Memilih di antara beberapa operasi channel yang siap untuk dijalankan.
- Kegunaan: Berguna untuk menangani beberapa channel sekaligus dalam sebuah goroutine.
Example Code:
package main
import (
"fmt"
"time"
)
func main() {
ch1 := make(chan string)
ch2 := make(chan string)
go func() {
time.Sleep(2 * time.Second)
ch1 <- "one"
}()
go func() {
time.Sleep(1 * time.Second)
ch2 <- "two"
}()
for i := 0; i < 2; i++ {
select {
case msg1 := <-ch1:
fmt.Println("received", msg1)
case msg2 := <-ch2:
fmt.Println("received", msg2)
}
}
}
5. Context
- Deskripsi: Menggunakan
context
untuk mengelola timeout, cancel, dan cross data API dan goroutine. - Kegunaan: Berguna untuk mengontrol eksekusi goroutine terutama pada web service atau API yang harus menangani permintaan yang mungkin memakan waktu lama atau terputus di tengah jalan.
Example Code:
package main
import (
"context"
"fmt"
"time"
)
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
ch := make(chan string)
go func() {
time.Sleep(2 * time.Second)
ch <- "operation result"
}()
select {
case res := <-ch:
fmt.Println(res)
case <-ctx.Done():
fmt.Println("operation timed out")
}
}
Dengan memahami dan menerapkan concurrency pattern di atas, kalian bisa menulis kode yang lebih efisien dan dapat diskalakan dengan lebih baik dalam Golang.