Chuyển đến nội dung chính
Mô-đun 7 · Bài học 1 trong tổng số 531/50 trong khóa học~12 min
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
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

Go
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:

Go
// 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 propagati

Lỗ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:

Go
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:

Go
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ạnhGoroutineChủ đề hệ điều hành
Ngăn xếp ban đầu~2 KB1–8 MB (được phân bổ khi bắt đầu)
Sáng tạonano giâymicro 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ó

tập thể dục#go.m7.l1.e1
Nỗ lực: 0Đang tải…

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.

Đang tải trình chỉnh sửa…
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ử

tập thể dục#go.m7.l1.e2
Nỗ lực: 0Đang tải…

Khởi chạy một hàm ẩn danh dưới dạng goroutine in 'hi'.

Đang tải trình chỉnh sửa…
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ử

Quiz#go.m7.l1.e3
Sẵn sàng

Điều gì xảy ra nếu `main` thoát trước khi goroutine hoàn thành?

Go
func main() {
  go work()
  // main ritorna immediatamente
}
Tùy chọn trả lời

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.
  • main không chờ đợi: bạn cần đồng bộ hóa (kênh/Nhóm chờ/ngữ cảnh).
  • time.Sleep chỉ 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 for là "mới" trên mỗi lần lặp (không còn lỗi "3 3 3").