keyboard_tabSkip to content
listGoAPI Essential

รู้จักกับ channel

สารบัญ

Channel คืออะไร

Channel ใน Go คือ ช่องทางที่ใช้สำหรับการสื่อสารระหว่าง Goroutines ด้วยกัน โดย Channel นั้นสามารถส่งข้อมูลได้ทั้งแบบ synchronous และแบบ asynchronous

  • Synchronous คือ โปรแกรมที่คำสั่งต่าง ๆ ทำงานตามลำดับ โดยคำสั่งหนึ่งจะทำงานต่อได้ก็ต่อเมื่อคำสั่งก่อนหน้าทำงานเสร็จสิ้นแล้ว
  • Asynchronous คือ โปรแกรมที่คำสั่งต่าง ๆ สามารถทำงานพร้อมกันได้ โดยคำสั่งหนึ่ง "ไม่จำเป็นต้องรอ" ให้คำสั่งก่อนหน้าทำงานเสร็จสิ้นก่อนจึงจะทำงานต่อได้

ซึ่ง Channel สามารถใช้งานได้ทั้ง 2 แบบ เปรียบเสมือนมีท่อหนึ่งที่ใช้สำหรับส่งข้อมูล ลองมาดูตัวอย่าง code ทั้ง 2 แบบ

ตัวอย่าง code channel

Operator ที่ใช้จัดการ Channel

  • <- = Operator นี้ใช้สำหรับการส่งและรับข้อมูลระหว่าง Goroutines ด้วยกัน โดย
  • Goroutine ที่ส่งข้อมูลจะใช้ operator <- กับตัวแปร Channel โดยให้ข้อมูลที่ต้องการส่งเป็น argument แรก
  • ส่วน Goroutine ที่รับข้อมูลจะใช้ operator <- กับตัวแปร Channel โดยไม่มีการให้ argument ใด ๆ

ตัวอย่าง code

go
package main

import (
  "fmt"
)

func main() {
  // สร้าง Channel ใหม่
  ch := make(chan int)

  // ส่งข้อมูลไปยัง Channel
  ch <- 10

  // รับข้อมูลจาก Channel
  v := <-ch

  // พิมพ์ข้อมูล
  fmt.Println(v)
}

เราจะมาลองดูตัวอย่างการส่งทั้งแบบ Synchronous และ Asynchronous ใน channel กัน

Synchronous

go
package main

import (
  "fmt"
)

func main() {
  // สร้าง Channel ใหม่
  ch := make(chan int, 1)

  // ส่งข้อมูลไปยัง Channel
  ch <- 1

  // รับข้อมูลจาก Channel
  v := <-ch

  // พิมพ์ข้อมูล
  fmt.Println(v)
}

Asynchronous

go
package main

import (
  "fmt"
)

func main() {
  // สร้าง Channel ใหม่
  ch := make(chan int)

  // สร้าง Goroutine ใหม่
  go func() {
    // ส่งข้อมูลไปยัง Channel
    ch <- 1
  }()

  // รับข้อมูลจาก Channel
  v := <-ch

  // พิมพ์ข้อมูล
  fmt.Println(v)
}

Channel สามารถใช้กับเหตุการณ์ต่าง ๆ ดังนี้

  1. การแชร์ข้อมูลระหว่าง Goroutines = Channel สามารถใช้สำหรับแชร์ข้อมูลระหว่าง Goroutines ด้วยกัน โดย Goroutines ใด ๆ ก็ตามที่สามารถเข้าถึง Channel นั้น ๆ ก็สามารถส่งและรับข้อมูลได้
  2. การ Synchonize การทำงานระหว่าง Goroutines = Channel สามารถใช้สำหรับ Synchonize การทำงานระหว่าง Goroutines ด้วยกัน โดย Goroutines ใด ๆ ก็ตามที่ต้องการรอให้ Goroutines อื่น ๆ ทำงานเสร็จสิ้นก่อน จึงจะทำงานต่อได้ สามารถใช้ Channel ในการรอคอย
  3. การรอเหตุการณ์บางอย่าง = Channel สามารถใช้สำหรับรอเหตุการณ์บางอย่าง เช่น รอให้ผู้ใช้กดปุ่ม รอให้ข้อมูลพร้อมใช้งาน เป็นต้น

buffer channel

Buffer channel ใน Go คือ Channel ที่มี Buffer อยู่ภายใน โดย Buffer ทำหน้าที่เก็บข้อมูลไว้ชั่วคราว ช่วยให้การส่งข้อมูลระหว่าง Goroutines เป็นไปอย่างราบรื่นยิ่งขึ้น

Buffer channel สามารถสร้างได้โดยใช้ฟังก์ชัน make() โดยให้ argument ที่สองเป็นจำนวน Buffer ที่ต้องการ

bash
ch := make(chan int, 10)
  • Channel ch จะมี Buffer ขนาด 10 ตัว

ตัวอย่าง code

go
package main

import (
  "fmt"
)

func main() {
  // สร้าง Buffer channel ขนาด 10 ตัว
  ch := make(chan int, 10)

  // ส่งข้อมูลไปยัง Buffer channel
  for i := 0; i < 10; i++ {
    ch <- i
  }

  // รับข้อมูลจาก Buffer channel
  for i := 0; i < 10; i++ {
    fmt.Println(<-ch)
  }
}

ผลลัพธ์ก็จะได้ออกมาเป็น

0
1
2
3
4
5
6
7
8
9

ประโยชน์ของ Buffer channel

  • ช่วยให้การส่งข้อมูลระหว่าง Goroutines เป็นไปอย่างราบรื่นยิ่งขึ้น โดย Goroutine ที่ส่งข้อมูลไม่จำเป็นต้องรอให้ Goroutine ที่รับข้อมูลว่างเปล่าก่อนจึงจะสามารถส่งข้อมูลได้
  • ช่วยลดปัญหา Deadlock โดย Goroutine ที่ส่งข้อมูลจะไม่ค้างหาก Buffer เต็ม

Limitation

  • หาก Buffer เต็ม Goroutine ที่ส่งข้อมูลจะค้างจนกว่าจะมีข้อมูลถูกรับออกไปจาก Buffer
  • หาก Buffer ว่าง Goroutine ที่รับข้อมูลจะค้างจนกว่าจะมีข้อมูลถูกส่งเข้ามาใน Buffer

Select Statement

Select statement ของ channel ใน Go คือ statement ที่ใช้สำหรับการเลือกการทำงานระหว่าง Goroutines ด้วยกัน โดย Goroutine ที่ทำงานกับ Channel สามารถใช้ select statement เพื่อเลือกการทำงานระหว่างการส่งข้อมูลไปยัง Channel และการรอรับข้อมูลจาก Channel

go
select {
  case v := <-ch:
    // ทำงานเมื่อได้รับข้อมูลจาก Channel
  case ch <- v:
    // ทำงานเมื่อส่งข้อมูลไปยัง Channel
}

ตัวอย่างการใช้งานใน code

go
package main

import (
  "fmt"
  "time"
)

func main() {
  ch := make(chan string)

  go func() {
    ch <- "Hello, world!"
  }()

  time.Sleep(1 * time.Second)

  select {
  case msg := <-ch:
    fmt.Println(msg)
  default:
    fmt.Println("No message received")
  }
}