ข้ามไปยังเนื้อหา

Channels

Channel คือท่อส่งข้อมูลแบบ typed ที่ goroutines ใช้สื่อสาร การส่งค่าเข้า channel และรับจาก channel เป็นสองคำสั่งที่ถ่ายโอนทั้งข้อมูลและการ synchronize ในขั้นตอนเดียว

ch := make(chan int) // unbuffered channel ของ int
ch <- 42 // ส่ง (บล็อกจนกว่า receiver พร้อม)
v := <-ch // รับ (บล็อกจนกว่า sender พร้อม)

Type system บังคับสิ่งที่ไหลผ่าน channel chan string ไม่ยอมรับ int

UnbufferedBuffered
สร้างด้วยmake(chan T)make(chan T, capacity)
Send บล็อก?ใช่ — จนกว่า receiver พร้อมไม่ — จนกว่า buffer เต็ม
Receive บล็อก?ใช่ — จนกว่า sender พร้อมไม่ — จนกว่า buffer ว่าง
Synchronize?ใช่ — sender และ receiver พบกันในเวลาเดียวกันไม่ — buffer แยกเวลาออกจากกัน

Unbuffered channels ให้ synchronization guarantee ที่แข็งแกร่งที่สุด: sender รู้ว่า receiver ได้รับค่าแล้วก่อนที่จะดำเนินต่อ

คุณสามารถจำกัด channel parameter ให้เป็น send-only หรือ receive-only เพื่อแสดง intent ใน type:

func producer(out chan<- int) { out <- 1 } // send-only
func consumer(in <-chan int) { v := <-in; _ = v } // receive-only

Sender เรียก close(ch) เพื่อบอกว่าจะไม่มีค่าอีกแล้ว Receivers ใช้ for range loop เพื่อดึงค่าทั้งหมดจนกว่าจะ close:

close(ch)
for v := range ch { // exit เมื่อ ch ถูกปิดและว่าง
fmt.Println(v)
}

Receive expression สามารถ return boolean ตัวที่สองที่บอกว่า channel ยังเปิดอยู่หรือไม่:

v, ok := <-ch
// ok == true → v เป็นค่าจริง
// ok == false → channel ถูกปิดและดึงออกหมดแล้ว v เป็น zero value
package main
import "fmt"
func producer(ch chan<- int, n int) {
for i := 1; i <= n; i++ {
ch <- i
}
close(ch)
}
func main() {
// Unbuffered channel: producer รันใน goroutine
ch := make(chan int)
go producer(ch, 5)
for v := range ch {
fmt.Println(v)
}
// Buffered channel: ส่งทั้งหมดพอดี buffer
buf := make(chan string, 3)
buf <- "a"
buf <- "b"
buf <- "c"
close(buf)
for s := range buf {
fmt.Println(s)
}
// Comma-ok: แยกค่าจริงจาก zero value ของ channel ที่ปิด
done := make(chan bool, 1)
done <- true
close(done)
v1, ok1 := <-done
fmt.Println(v1, ok1) // true true
v2, ok2 := <-done
fmt.Println(v2, ok2) // false false
}
Unbuffered channel — rendez-vous point:
G1 ──send──→ [channel] ←──recv── G2
(block) (block)
G1 และ G2 ต้องพบกัน
Buffered channel (cap=3):
G1 ──send──→ [_][_][_] ←──recv── G2
(block เมื่อ full)
(block เมื่อ empty)
G1/G2 ทำงานอิสระมากกว่า
สิ่งที่ได้ประโยชน์ต้นทุน
unbuffered channelsynchronization guarantee แข็งแกร่งG ส่ง block จนกว่า G รับพร้อม
buffered channeldecouple sender/receiver timingต้อง size buffer ให้เหมาะ, อาจ hide backpressure
channel direction (chan<-, <-chan)compile-time safetyverbose ใน function signature
close + rangenotify completion ง่ายส่งหลัง close → panic
  • channel เสมอเร็วกว่า mutex — channel มี overhead มากกว่า mutex สำหรับ simple counter
  • buffered channel ช่วยป้องกัน deadlock — ไม่เสมอ — deadlock ยังเกิดได้ถ้า buffer เต็มและไม่มี receiver
  • close(ch) ลบข้อมูลใน buffer — ไม่ — receiver ยังรับข้อมูลที่เหลือใน buffer ได้หลัง close
  • สามารถ close channel จาก receiver ได้ — sender เท่านั้นที่ควร close channel

💡 ตัวอย่างจากของจริง

net/http ใช้ channel pattern ภายใน สำหรับ read/write pipeline ระหว่าง connection handler กับ request body

Kubernetes ใช้ event channel ส่ง object changes ไป controller loop

Docker ใช้ channel เชื่อม container process กับ log collector สำหรับ container log streaming

เกิดอะไรขึ้นเมื่อคุณรับจาก unbuffered channel ขณะที่ไม่มี goroutine กำลังส่ง?
คุณเรียก close(ch) บน buffered channel ที่ยังมี 3 items อยู่ใน buffer เกิดอะไรขึ้นเมื่อ range?
return value ตัวที่สอง ok ใน `v, ok := <-ch` บอกอะไร?
annotation ทิศทาง channel ใดที่จำกัด parameter ให้รับเท่านั้น?