Chuyển đến nội dung chính
Mô-đun 7 · Bài học 2 trong tổng số 532/50 trong khóa học~15 min
Bài học theo mô-đun (2/5)

Kênh: giao tiếp đánh máy

Kênh là cơ chế đặc trưng để giao tiếp giữa goroutine. Triết lý cờ vây (Rob Pike):

"Đừng giao tiếp bằng cách chia sẻ bộ nhớ; hãy chia sẻ bộ nhớ bằng cách giao tiếp."

Kênh là hàng đợi FIFO được nhập, loại an toàn và an toàn cho hoạt động đồng thời sử dụng. Kết hợp với go, đây là khối xây dựng chính của cờ vây sự đồng thời.

Tạo, gửi, nhận

Go
ch := make(chan int)     // canale di int, unbuffered

go func() {
    ch <- 42             // invia (può bloccare)
}()

v := <-ch                // ricevi (può bloccare)
fmt.Println(v)           // 42

Cú pháp:

  • make(chan T) hoặc make(chan T, N) để lưu vào bộ đệm.
  • ch <- v gửi.
  • v := <-ch nhận được.
  • v, ok := <-ch nhận được có gắn cờ (ok = false nếu kênh bị đóng VÀ trống).

Không có bộ đệm: điểm hẹn

Khối kênh không có bộ đệm (dung lượng 0):

  • người gửi cho đến khi người nhận sẵn sàng;
  • người nhận cho đến khi người gửi sẵn sàng.

Đó là cuộc hẹn đồng bộ: hữu ích như một “tín hiệu” hoặc để đảm bảo rằng giá trị đã được chuyển giao.

Được đệm: xếp hàng lên tới N

Go
ch := make(chan int, 3)
ch <- 1   // non blocca
ch <- 2   // non blocca
ch <- 3   // non blocca
ch <- 4   // BLOCCA: il buffer è pieno

Người gửi chỉ chặn khi bộ đệm đầy; chỉ người nhận khi nó trống rỗng.

gần và tầm xa

người gửi đóng kênh khi không còn giá trị nào:

Go
ch := make(chan int, 3)
ch <- 1
ch <- 2
ch <- 3
close(ch)

for v := range ch {      // termina quando ch è chiuso E vuoto
    fmt.Println(v)
}

for range lặp lại trên một kênh cho đến khi kênh bị đóng (và cạn kiệt). Đây là mẫu rõ ràng nhất để "tiêu thụ mọi thứ đến".

Tính định hướng trong chữ ký

Các tham số chức năng có thể hạn chế hướng:

Go
func producer(out chan<- int) {   // solo invio
    out <- 1
    close(out)
}

func consumer(in <-chan int) {    // solo ricezione
    for v := range in {
        fmt.Println(v)
    }
}

chan<- T = chỉ gửi, <-chan T = chỉ nhận. Hai chiều kênh chan T có thể được chuyển sang một trong hai kênh (chuyển đổi ngầm). Nó cải thiện sự rõ ràng và ngăn ngừa sai lầm.

Các mẫu phổ biến (xem trước)

  • Nhóm công nhân: N goroutine tiêu thụ từ jobs <-chan T, sản xuất vào results chan<- R.
  • Đường ống: các giai đoạn được kết nối bằng kênh.
  • Tín hiệu hoàn tất: kênh chan struct{} đóng để báo hiệu "dừng".
Go
done := make(chan struct{})
go func() {
    <-done    // blocca finché qualcuno chiude done
    cleanup()
}()
close(done)   // segnale di stop

chan struct{} là kênh "tín hiệu": tải trọng bằng 0 byte, chỉ đồng bộ hóa.

Hãy thử nó

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

Tạo một kênh int không có bộ đệm, gửi 42 từ goroutine, nhận vào main và in nó.

Đang tải trình chỉnh sửa…
Hiển thị gợi ý

Không có bộ đệm, người gửi và người nhận sẽ đồng bộ hóa.

Giải pháp khả dụng sau 3 lần thử

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

Gửi 1,2,3 qua kênh đệm, đóng nó, sau đó in mọi thứ bằng for-range.

Đang tải trình chỉnh sửa…
Hiển thị gợi ý

`cho phạm vi` trên một kênh kết thúc khi kênh bị đóng và cạn kiệt.

Giải pháp khả dụng sau 3 lần thử

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

Kênh UNBUFFERED sẽ làm gì khi người gửi gửi nhưng chưa có người nhận nào sẵn sàng?

Go
ch := make(chan int)
ch <- 1 // ?
Tùy chọn trả lời

Tóm tắt lại

  • Channel = gõ hàng đợi FIFO, an toàn giữa các goroutines.
  • Unbuffered: rendezvous (đồng bộ người gửi và người nhận).
  • Buffered: tách rời, chỉ chặn khi đầy/trống.
  • close(ch): chỉ người gửi; for range tiêu thụ cho đến khi đóng.
  • Tính định hướng: chan<- T (chỉ gửi), <-chan T (chỉ nhận).
  • chan struct{} làm kênh tín hiệu để đồng bộ hóa thuần túy.