Goroutine và Channel

Goroutine — Luồng nhẹ của Go

Goroutine là luồng thực thi nhẹ, do Go runtime quản lý. Tạo goroutine cực đơn giản:

go funcName()           // chạy funcName() trong goroutine mới
go func() {             // anonymous goroutine
    fmt.Println("Hello từ goroutine")
}()

// Goroutine rất nhẹ: ~2KB stack ban đầu (thread OS: ~1MB)
// Có thể tạo hàng triệu goroutine mà không lo hết memory

Channel — Giao tiếp giữa Goroutine

// Tạo channel
ch := make(chan int)       // unbuffered: gửi & nhận phải đồng thời
ch2 := make(chan int, 5)   // buffered: buffer 5 phần tử

// Gửi vào channel
ch <- 42

// Nhận từ channel
v := <-ch
v, ok := <-ch   // ok = false nếu channel đã đóng

// Đóng channel (chỉ sender đóng)
close(ch)

// Duyệt channel đến khi đóng
for v := range ch {
    fmt.Println(v)
}

Producer-Consumer Pattern

func producer(ch chan<- int, n int) {  // ch chỉ gửi
    for i := 0; i < n; i++ {
        ch <- i * i
    }
    close(ch)
}

func consumer(ch <-chan int) {         // ch chỉ nhận
    for v := range ch {
        fmt.Println("Nhận:", v)
    }
}

func main() {
    ch := make(chan int, 10)
    go producer(ch, 5)
    consumer(ch)  // block đến khi channel đóng
}

sync.WaitGroup — Chờ nhiều Goroutine

var wg sync.WaitGroup

for i := 0; i < 5; i++ {
    wg.Add(1)
    go func(id int) {
        defer wg.Done()
        fmt.Printf("Worker %d xong\n", id)
    }(i)
}
wg.Wait()  // block đến khi tất cả Done()

select — Lắng nghe nhiều Channel

select {
case msg := <-ch1:
    fmt.Println("Nhận từ ch1:", msg)
case msg := <-ch2:
    fmt.Println("Nhận từ ch2:", msg)
case <-time.After(1 * time.Second):
    fmt.Println("Timeout!")
default:
    fmt.Println("Không có gì")
}

Mutex — Bảo vệ dữ liệu chia sẻ

type SafeCounter struct {
    mu    sync.Mutex
    count int
}

func (c *SafeCounter) Inc() {
    c.mu.Lock()
    defer c.mu.Unlock()
    c.count++
}

func (c *SafeCounter) Value() int {
    c.mu.Lock()
    defer c.mu.Unlock()
    return c.count
}
example.go
package main

import (
	"fmt"
	"sync"
	"time"
)

type Result struct {
	URL  string
	Time time.Duration
	Err  error
}

// Giả lập fetch URL
func fetch(url string) Result {
	start := time.Now()
	// Mô phỏng latency khác nhau
	delays := map[string]time.Duration{
		"api.example.com":   80 * time.Millisecond,
		"cdn.example.com":   20 * time.Millisecond,
		"db.example.com":    150 * time.Millisecond,
		"cache.example.com": 10 * time.Millisecond,
	}
	time.Sleep(delays[url])
	return Result{URL: url, Time: time.Since(start)}
}

func fetchAll(urls []string) []Result {
	results := make([]Result, len(urls))
	var wg sync.WaitGroup
	for i, url := range urls {
		wg.Add(1)
		go func(i int, url string) {
			defer wg.Done()
			results[i] = fetch(url)
		}(i, url)
	}
	wg.Wait()
	return results
}

func main() {
	urls := []string{
		"api.example.com",
		"cdn.example.com",
		"db.example.com",
		"cache.example.com",
	}

	fmt.Println("=== Sequential ===")
	start := time.Now()
	for _, url := range urls {
		r := fetch(url)
		fmt.Printf("%-25s %v\n", r.URL, r.Time)
	}
	fmt.Printf("Tổng thời gian: %v\n\n", time.Since(start))

	fmt.Println("=== Concurrent (goroutines) ===")
	start = time.Now()
	results := fetchAll(urls)
	for _, r := range results {
		fmt.Printf("%-25s %v\n", r.URL, r.Time)
	}
	fmt.Printf("Tổng thời gian: %v\n", time.Since(start))
}