Goroutine, WaitGroup & Mutex
Ada permasalahan ketika kita tidak benar dalam mengguakan goroutines yaitu race codition dan deadlock. Race condition adalah ketika antar goroutine saling bersaing untuk melakukan sebuah eksekusi dan memodifikasi data yang sama sehingga data yang di modifikasi tidak valid dan hasilnya akan selalu tidak sama. Dibawah ini contoh race dan hasil dari counter akan berbeda beda setiap kali eksekusinya.
package main
import (
"fmt"
"time"
)
var counter = 0
func increment() {
temp := counter
time.Sleep(time.Millisecond) // Simulasi operasi yang memerlukan waktu
counter = temp + 1
}
func main() {
for i := 0; i < 5; i++ {
go increment()
}
time.Sleep(time.Second) // Menunggu goroutine selesai
fmt.Println("Nilai counter akhir:", counter)
}
Selain itu ada masalah lain yaitu deadlock yaitu ketika ada 2 atau lebih goroutine gagal berkomunikasi karena saling menunggu satu dengan lainnya.
WaitGroup
WaitGroup terdapat pada package sync dari Go digunakan untuk menunggu goroutine agar selesai melakukan eksekusi. Contoh penggunaannya.
package main
import (
"fmt"
"sync"
)
func main() {
var wg sync.WaitGroup
for i := 0; i < 5; i++ {
wg.Add(1)
go worker(i, &wg)
}
wg.Wait()
fmt.Println("Semua goroutine selesai.")
}
func worker(id int, wg *sync.WaitGroup) {
defer wg.Done()
fmt.Printf("Goroutine %d sedang bekerja\n", id)
}
Mutex
Mutex (Mutual Exclusion) adalah mekanisme lain yang digunakan untuk mengamankan akses ke data bersamaan. Hanya satu goroutine yang dapat mengunci mutex pada suatu waktu, memastikan bahwa data tidak dimodifikasi oleh goroutine lain sampai mutex dilepaskan. Contoh penggunaan mutex.
Tanpa mutex
package main
import (
"fmt"
"sync"
)
var counter int
func main() {
var wg sync.WaitGroup
for i := 0; i < 10000; i++ {
wg.Add(1)
go increment(&wg)
}
wg.Wait()
fmt.Printf("Nilai counter akhir: %d\n", counter)
}
func increment(wg *sync.WaitGroup) {
defer wg.Done()
counter++
}
Hasil :
Dengan mutex
package main
import (
"fmt"
"sync"
)
var counter int
var mutex sync.Mutex
func main() {
var wg sync.WaitGroup
for i := 0; i < 10000; i++ {
wg.Add(1)
go increment(&wg)
}
wg.Wait()
fmt.Printf("Nilai counter akhir: %d\n", counter)
}
func increment(wg *sync.WaitGroup) {
defer wg.Done()
mutex.Lock()
counter++
mutex.Unlock()
}
Hasil:
Jika tanpa mutex maka code diatas akan menampilkan counter yang selalu berbeda beda setiap kali eksekusinya, ini terjadi karena ada race condition sehingga hasil akhirnya tidak selalu sama.