Bài học theo mô-đun (1/5)
Goroutines: song song nhẹ
goroutine là một đơn vị thực thi đồng thời được quản lý bởi Go
thời gian chạy. Bạn khởi chạy một hàm với từ khóa go trước một hàm
gọi:
go greet("Anna") // chiamata a funzione esistente
go func() { // funzione anonima
fmt.Println("hi")
}()Câu lệnh go trả về ngay lập tức: goroutine bắt đầu sau
song song và người gọi tiếp tục.
Tại sao chúng "nhẹ"
- Ngăn xếp ban đầu ~2KB (tăng linh hoạt, lên tới hàng trăm MB nếu cần).
- Ghép kênh trên một số lượng nhỏ luồng hệ điều hành theo thời gian chạy (
GOMAXPROCS). - Hàng nghìn/hàng triệu goroutines là chuyện thường ngày; với các luồng hệ điều hành thì điều đó là không thể.
Về mặt khái niệm, nó là "cháy và quên đồng thời", nhưng với tất cả
các công cụ đặc trưng để phối hợp (kênh, sync.WaitGroup,
context).
chính không chờ đợi
func main() {
go func() { fmt.Println("ciao dalla goroutine") }()
// main esce subito: niente garanzia che la goroutine completi
}Khi main trả về, quy trình kết thúc: goroutines không nhận được
để "hoàn thành công việc của mình", họ chỉ đơn giản là bị giết cùng với
quá trình. Cần phải đồng bộ hóa rõ ràng.
Đồng bộ hóa: bản xem trước
Ba công cụ bạn sẽ thấy trong các bài học tiếp theo:
// 1) Channel: la goroutine "segnala" via canale
done := make(chan struct{})
go func() {
work()
done <- struct{}{}
}()
<-done // attendi
// 2) sync.WaitGroup: N goroutine
var wg sync.WaitGroup
wg.Add(1)
go func() { defer wg.Done(); work() }()
wg.Wait()
// 3) context: cancellazione/timeout propagatiLỗi đóng trong vòng lặp "cổ điển"
Cho đến phiên bản 1.21, đây là một vấn đề thường gặp:
for i := 0; i < 3; i++ {
go func() { fmt.Println(i) }() // stampa "3 3 3" su versioni vecchie
}Từ Go 1.22 biến for được tạo mới trên mỗi biến
lặp đi lặp lại, do đó lỗi được sửa theo mặc định. Trong các cơ sở mã cũ hơn, bạn sẽ
vẫn thấy mẫu sửa lỗi:
for i := 0; i < 3; i++ {
i := i // shadow esplicito
go func() { fmt.Println(i) }()
}
// oppure passa come argomento:
for i := 0; i < 3; i++ {
go func(n int) { fmt.Println(n) }(i)
}Goroutine != Chuỗi hệ điều hành
| Khía cạnh | Goroutine | Chủ đề hệ điều hành |
|---|---|---|
| Ngăn xếp ban đầu | ~2 KB | 1–8 MB (được phân bổ khi bắt đầu) |
| Sáng tạo | nano giây | micro giây |
| Lập kế hoạch | Đi thời gian chạy (hợp tác) | hạt nhân (ưu tiên) |
| Đếm thực tế | 100K+ | vài nghìn |
Một luồng hệ điều hành có thể lưu trữ nhiều goroutines. Thời gian chạy di chuyển goroutines trên các luồng khi cần thiết.
Hãy thử nó
Khởi chạy chức năng chào dưới dạng goroutine và đợi 100 mili giây theo thời gian. Ngủ trước khi thoát khỏi chính.
Hiển thị gợi ý
`go` trước lệnh gọi hàm sẽ khởi chạy goroutine.
Giải pháp khả dụng sau 3 lần thử
Khởi chạy một hàm ẩn danh dưới dạng goroutine in 'hi'.
Hiển thị gợi ý
Một hàm ẩn danh được gọi với dấu `()`: `go func(){...}()`.
Giải pháp khả dụng sau 3 lần thử
Điều gì xảy ra nếu `main` thoát trước khi goroutine hoàn thành?
func main() {
go work()
// main ritorna immediatamente
}Tóm tắt lại
go f()khởi chạy đồng thời f(); nó quay lại ngay lập tức.- Goroutine = ~2 KB ban đầu, được ghép kênh trên một số luồng hệ điều hành.
mainkhông chờ đợi: bạn cần đồng bộ hóa (kênh/Nhóm chờ/ngữ cảnh).time.Sleepchỉ mang tính mô phạm, không bao giờ đồng bộ hóa trong sản xuất.- Từ Phiên bản 1.22, biến
forlà "mới" trên mỗi lần lặp (không còn lỗi "3 3 3").