Skip to main content

รู้จักกับ 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

package main

import (
"fmt"
)

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

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

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

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

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

Synchronous​

package main

import (
"fmt"
)

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

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

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

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

Asynchronous​

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 ที่ต้องการ

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

ตัวอย่าง code

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

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

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

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")
}
}