99. Concurrency vs Parallelism & Go Scheduler
Perbedaan Concurrency dan Parallelism, serta arsitektur M:N Scheduler Go.
Concurrency vs Parallelism
- Concurrency: Menangani banyak hal sekaligus (1 kasir melayani antrean)
- Parallelism: Eksekusi fisik bersamaan pada multi-core CPU (4 kasir bekerja)
Goroutine sangat ringan: hanya butuh ~2 KB memori awal vs Thread OS ~1-2 MB!
Contoh program
package main
import (
"fmt"
"runtime"
)
func main() {
fmt.Println("CPU Cores:", runtime.NumCPU())
fmt.Println("Goroutines Aktif:", runtime.NumGoroutine())
}
100. Membuat Goroutines (keyword go)
Menjalankan fungsi secara asinkronus dengan keyword 'go'.
Keyword 'go'
Tambahkan go sebelum pemanggilan fungsi. Jika main() selesai duluan, goroutine anak langsung dihentikan paksa!
Contoh program
package main
import (
"fmt"
"time"
)
func DisplayNumber(n int) {
fmt.Println("Goroutine:", n)
}
func main() {
for i := 1; i <= 5; i++ {
go DisplayNumber(i)
}
fmt.Println("Main selesai!")
time.Sleep(100 * time.Millisecond)
}
101. Channels (Kirim & Terima Data antar Goroutine)
Media komunikasi data antar Goroutine secara aman.
Channel
- Buat:
ch := make(chan string) - Kirim:
ch <- "Data" - Terima:
data := <-ch - Tutup:
close(ch)
Unbuffered Channel bersifat blocking.
Contoh program
package main
import (
"fmt"
"time"
)
func main() {
ch := make(chan string)
go func() {
time.Sleep(100 * time.Millisecond)
ch <- "Pesan dari Goroutine!"
}()
data := <-ch
fmt.Println("Diterima:", data)
}
102. Channel Direction & Buffered Channels
Membatasi arah channel (chan<- / <-chan) dan channel dengan kapasitas buffer.
Channel Direction & Buffer
chan<- string— In-Only (hanya kirim)<-chan string— Out-Only (hanya terima)make(chan string, 3)— Buffered (kapasitas 3)
Contoh program
package main
import "fmt"
func OnlyIn(ch chan<- string) { ch <- "Hello" }
func OnlyOut(ch <-chan string) { fmt.Println(<-ch) }
func main() {
ch := make(chan string)
go OnlyIn(ch)
OnlyOut(ch)
}
103. Range & Close Channel, Select Multiplexing
Iterasi stream data dengan for range, menutup channel, dan select untuk multi-channel.
Range, Close, & Select
for data := range ch mengambil data sampai channel ditutup. select mendengarkan beberapa channel yang pertama siap.
Contoh program
package main
import "fmt"
func main() {
ch := make(chan string, 3)
go func() {
for i := 1; i <= 3; i++ {
ch <- fmt.Sprintf("Item-%d", i)
}
close(ch)
}()
for data := range ch {
fmt.Println("Diterima:", data)
}
}
104. Race Condition & Data Race Problem
Bahaya konflik data saat banyak Goroutines memodifikasi variabel yang sama.
Race Condition
Terjadi ketika beberapa Goroutines mengubah data yang sama tanpa penguncian. Deteksi: go test -race atau go run -race main.go.
Contoh program
package main
import (
"fmt"
"time"
)
func main() {
x := 0
for i := 0; i < 1000; i++ {
go func() {
for j := 0; j < 100; j++ { x++ }
}()
}
time.Sleep(500 * time.Millisecond)
fmt.Println("x (harusnya 100000, tapi race condition):", x)
}
105. sync.Mutex (Mutual Exclusion Lock)
Mengamankan data dari Race Condition dengan penguncian Mutex Lock/Unlock.
sync.Mutex
mutex.Lock() mengunci area kritikal. mutex.Unlock() melepas kunci. Hanya 1 Goroutine boleh masuk.
Contoh program
package main
import (
"fmt"
"sync"
"time"
)
func main() {
var mu sync.Mutex
x := 0
for i := 0; i < 1000; i++ {
go func() {
for j := 0; j < 100; j++ {
mu.Lock()
x++
mu.Unlock()
}
}()
}
time.Sleep(500 * time.Millisecond)
fmt.Println("x aman (tepat 100000):", x)
}
106. sync.RWMutex (Reader-Writer Lock)
Optimasi performa kuncian dengan memisahkan Read Lock dan Write Lock.
sync.RWMutex
RLock()/RUnlock()— banyak reader sekaligusLock()/Unlock()— hanya 1 writer
Contoh program
package main
import (
"fmt"
"sync"
)
type SafeAccount struct {
rw sync.RWMutex
balance int
}
func (a *SafeAccount) GetBalance() int {
a.rw.RLock()
defer a.rw.RUnlock()
return a.balance
}
func main() {
acc := SafeAccount{balance: 500000}
fmt.Println("Saldo:", acc.GetBalance())
}
107. sync.WaitGroup (Menunggu Goroutines Selesai)
Cara standar menunggu penyelesaian Goroutines tanpa time.Sleep.
sync.WaitGroup
wg.Add(n)— tambah counterwg.Done()— kurangi counterwg.Wait()— tunggu counter = 0
Contoh program
package main
import (
"fmt"
"sync"
"time"
)
func Task(id int, wg *sync.WaitGroup) {
defer wg.Done()
fmt.Printf("Task %d bekerja...\n", id)
time.Sleep(50 * time.Millisecond)
}
func main() {
var wg sync.WaitGroup
for i := 1; i <= 5; i++ {
wg.Add(1)
go Task(i, &wg)
}
wg.Wait()
fmt.Println("SEMUA TASK SELESAI!")
}
108. sync.Once, sync.Map, & sync/atomic
Eksekusi tunggal (Once), Map thread-safe (sync.Map), dan operasi atomic.
Utilitas Concurrency
sync.Once— fungsi hanya dieksekusi 1 kalisync.Map— Map yang aman (Store/Load/Delete)sync/atomic— operasi matematika tanpa lock
Contoh program
package main
import (
"fmt"
"sync/atomic"
)
func main() {
var counter int64 = 0
atomic.AddInt64(&counter, 1)
atomic.AddInt64(&counter, 1)
fmt.Println("Atomic Counter:", atomic.LoadInt64(&counter))
}
109. Timer, Ticker, & GOMAXPROCS
Penjadwalan eksekusi satu kali (Timer), periodik (Ticker), dan pengaturan CPU cores.
Timer & Ticker
time.Timer— sinyal 1 kalitime.Ticker— sinyal periodik berulangruntime.GOMAXPROCS(n)— atur jumlah OS Thread
Contoh program
package main
import (
"fmt"
"time"
)
func main() {
ticker := time.NewTicker(100 * time.Millisecond)
done := make(chan bool)
go func() {
time.Sleep(350 * time.Millisecond)
done <- true
}()
for {
select {
case <-done:
ticker.Stop()
fmt.Println("Ticker Dihentikan!")
return
case t := <-ticker.C:
fmt.Println("Tick:", t.Format("15:04:05.000"))
}
}
}