Goroutines
Goroutine adalah unit eksekusi ringan yang berjalan secara concurrent di Go. Goroutine memungkinkan kita menjalankan fungsi secara asynchronous.
Contoh Masalah
Bagaimana cara:
- Menjalankan kode secara concurrent
- Mengelola multiple tasks
- 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
Basic Goroutine
- Keyword
go - Concurrent execution
- Non-blocking
- Keyword
Synchronization
- WaitGroup
- Mutex
- Channels
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