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))
}