← Danh sách bài học Bài 16/20

⚡ Bài 16: Goroutine (Concurrency)

⏱️ 25 phút | 📚 Nâng cao

🎯 Mục tiêu:

1. Goroutine Là Gì?

Goroutine là "lightweight thread" do Go runtime quản lý. Rất nhẹ, có thể tạo hàng nghìn goroutines mà không lo về memory.

package main

import (
    "fmt"
    "time"
)

func sayHello() {
    fmt.Println("Hello từ goroutine!")
}

func main() {
    go sayHello()  // Chạy trong goroutine riêng
    
    fmt.Println("Hello từ main!")
    time.Sleep(time.Second)  // Đợi goroutine chạy xong
}

2. Nhiều Goroutines

func printNumbers(name string) {
    for i := 1; i <= 3; i++ {
        fmt.Printf("%s: %d\n", name, i)
        time.Sleep(100 * time.Millisecond)
    }
}

func main() {
    go printNumbers("Goroutine 1")
    go printNumbers("Goroutine 2")
    
    time.Sleep(time.Second)
}
// Output xen kẽ nhau!

3. sync.WaitGroup

WaitGroup giúp đợi nhiều goroutines hoàn thành.

package main

import (
    "fmt"
    "sync"
)

func worker(id int, wg *sync.WaitGroup) {
    defer wg.Done()  // Báo đã xong
    fmt.Printf("Worker %d starting\n", id)
    // Làm việc gì đó...
    fmt.Printf("Worker %d done\n", id)
}

func main() {
    var wg sync.WaitGroup
    
    for i := 1; i <= 3; i++ {
        wg.Add(1)  // Thêm 1 vào counter
        go worker(i, &wg)
    }
    
    wg.Wait()  // Đợi tất cả Done
    fmt.Println("All workers finished!")
}
💡 Pattern:
1. wg.Add(1) trước khi go
2. defer wg.Done() trong goroutine
3. wg.Wait() để đợi tất cả

4. Anonymous Goroutine

var wg sync.WaitGroup

for i := 1; i <= 3; i++ {
    wg.Add(1)
    go func(n int) {  // Truyền i vào
        defer wg.Done()
        fmt.Println("Number:", n)
    }(i)  // Gọi ngay với giá trị i
}

wg.Wait()
⚠️ Cẩn thận: Nếu không truyền i vào, goroutines sẽ share cùng biến i!

5. Race Condition

// SAI - có race condition!
counter := 0
for i := 0; i < 1000; i++ {
    go func() {
        counter++  // Nhiều goroutines đọc/ghi cùng lúc
    }()
}
// counter có thể không = 1000!

// ĐÚNG - dùng sync.Mutex
var mu sync.Mutex
var counter int

for i := 0; i < 1000; i++ {
    go func() {
        mu.Lock()
        counter++
        mu.Unlock()
    }()
}

📝 Tóm Tắt