ภาษา Go ตอน 6 การใช้ Channel ร่วมกับ Go Routines

Chaiyarin Niamsuwan
odds.team
Published in
4 min readSep 16, 2018

จากบทที่แล้ว ในตอนที่ 5 เรารู้ว่า Go Routines คืออะไร ใช้ยังไง และทำไมมันถึงทำให้โปรแกรมของเรานั้นมันเร็วขึ้นกันแล้ว ใครยังไม่อ่าน เข้าไปอ่านได้ที่ Link ด้านล่างนี้ครับ

และในบทนี้ ต้องใช้พื้นฐานการประกาศตัวแปร และ การสร้าง Function ของภาษา Go เพื่อนๆ คนไหนที่ยังไม่อ่าน ก็สามารถอ่านได้จาก Link ข้างล่างเลยครับ

มาครับ คราวนี้ เราจะไปรู้จักกับ Channel กันว่ามันคืออะไร กันนะ และ ใช้ร่วมกับ Go Routines ยังไงไปลุยกันเลยครับ

Go Channel คืออะไร ?

Channel มันคือ ท่อการสื่อสารระหว่าง Go Routines กับ ตัวโปรแกรมหลัก หรือ Go Routines ตัวอื่นๆก็ได้ หรือพูดง่ายๆ ว่า

เวลาเราสั่งงาน Go Routines ให้ทำอะไรเนี้ย มันจะไปทำงานเบื้องหลัง คือแบ่งไปให้ CPU อีก Core ช่วยทำงาน อะไรทำนองนี้

ส่วนตัวโปรแกรมหลักก็ทำงานไป และทีนี้ เมื่อตัว Go Routines ทำงานเสร็จ แล้วต้องการส่งค่าอะไรบางอย่าง มาที่ตัวโปรแกรมหลัก

Channel นี่หละ จะเป็นท่อน้ำ ที่ไหลค่าเหล่านั้นจาก Go Routines มาตัวโปรแกรมหลัก และเอาค่าไปใช้งานต่อ

เกริ่นเรื่อง Go Routines เล็กน้อย

คืองี้ เมื่อตอนที่ 5 เรามีสถานการณ์ที่ให้เขียนโปรแกรมดังนี้ คือ ให้นาย B ช่วยนาย A ไปซื้อของ เมื่อใช้ 2 คนซื้อของ มันก็ต้องเร็วกว่าคนเดียวคือ เป็นเรื่องปกติ

สถานการณ์เดิมจากตอน 5

  • นาย B ซื้อแว่นตา ที่เซเว่น 1 วินาที
  • นาย B ซื้อนาฬิกา ที่เซนทรัล 1 วินาที
  • นาย A ซื้อผลไม้ ที่สยามพารากอน 1 วินาที
  • นาย A ซื้อรถยนต์ ที่ศูนย์โตโยต้า 1 วินาที
  • รวมทั้งสิ้น นาย A และ นาย B ใช้เวลาทั้งหมดร่วมกัน 2 วินาที

แต่เพื่อนๆ จะสังเกตว่า การที่นาย B ซื้อของ ช่วย นาย A แต่นาย B นั้น ก็ยังไม่ได้เอาของมา ให้นาย A เลย

มันเหมือนต่างคนต่างซื้อและก็จบไป ในบทความนี้ผมจะเขียน สถานการณ์ต่อจากเดิม คือ เมื่อ นาย B ซื้อของเสร็จแล้ว

จะต้องนำของมามอบให้นาย A ด้วย โดยนาย A จะรอ จนกว่าจะได้ของจากนาย B และจบ การทำงานของโปรแกรม ได้เป็นภาพแบบนี้

ภาพประกอบคำอธิบาย นาย A และ นาย B

วิธีใช้งาน Go Channel

จำลองสถานการณ์ใหม่ อย่างที่บอกไปข้างบน สถานการณ์ที่เราจะเขียนโปแกรมในครั้งนี้ ก็คือ หลังจากที่ นาย B ซื้อของเสร็จแล้ว จะต้องมาส่งของให้ นาย A ด้วย

และนาย A ก็จะรอรับของจาก นาย B เมื่อ นาย A ได้รับของแล้วเป็นการจบการทำงาน

ดังนั้นจะเขียนสถานการณ์ได้แบบนี้

  • นาย B ซื้อแว่นตา ที่เซเว่น 1 วินาที
  • นาย B ซื้อนาฬิกา ที่เซนทรัล 1 วินาที
  • เมื่อ นาย B ซื้อครบ นำของไปให้ นาย A
  • นาย A ซื้อผลไม้ ที่สยามพารากอน 1 วินาที
  • นาย A ซื้อรถยนต์ ที่ศูนย์โตโยต้า 1 วินาที
  • นาย A รอรับของจาก นาย B

เริ่มต้นเขียนโปรแกรมกันครับ

ก่อนที่จะไปลงเขียนโปรแกรมตามสถานการณ์ข้างบน มารู้จักการประกาศตัวแปรประเภท Channel กันเล็กน้อยครับ

โดยการประกาศตัวแปร Channel เขียนดังนี้

ch := make(chan string);

อธิบายจาก Code ก็คือ เรากำลังสร้างท่อ Channel ท่อนึง ที่ชื่อ ch ซึ่งท่อตัวนี้ จะมีแค่ข้อมูล ประเภท String ที่สามารถไหลเข้าออกจากท่อได้เท่านั้น ถ้าเราต้องการเปลี่ยนเป็น ประเภทอื่น เราก็แค่เปลี่ยนประเภท หลัง chan แบบนี้

ch := make(chan bool); // ลองเปลี่ยนประเภทตัวแปร

ต่อมาเป็นการใส่ค่าเข้าตัวแปร ch หรือ เข้าท่อ ของ Channel ทำแบบนี้

ch <- "กำลังส่งของให้ นาย A";

จาก Code คือการ ใส่ข้อความให้ไหลไปตามท่อ เพื่อบอกให้นาย A ได้รู้ ว่าส่งของไปแล้วนะ

การรับค่าตัวแปรจาก Channel (นาย A มารับของ)

misterA := <- ch

จาก Code นี่ก็คือวิธีรับของจาก ท่อที่ไหลออกมาครับ ณ ที่นี้นาย A ก็จะได้คำว่า “กำลังส่งของให้ นาย A” ออกมา

ตอนนี้เราก็ได้รู้เรื่องราวพื้นฐานของ Channel แล้ว มาเขียนโค้ด ตามสถานการณ์ตัวอย่างข้างบนกันครับ

เราจะนำ Source Code จากตอนที่ 5 มาเพิ่มเติมตามสถานการณ์ใหม่เล็กน้อยดังนี้

Step 1) เปิดไฟล์ main.go จากตอน 5 ขึ้นมาได้เลยครับ และ สร้าง Function ชื่อว่า “sendToMisterA()” ภายใน Function นี้รับ Parameter 1 ตัว เป็นประเภท Channel โดยข้อมูลที่ไหลผ่าน Channel เป็น String จะเขียนได้หน้าตาแบบนี้

[สร้าง Function sendToMisterA()]func sendToMisterA(message chan<- string) {
time.Sleep(1 * time.Second)
message <- "กำลังส่งของให้ นาย A"
}

Step 2) นำ Function sendToMisterA() มาใช้งาน และถ้ารวม Source Code หมดแล้วจะได้ Code ตามด้านล่างนี้

[หน้าตาไฟล์ main.go ทั้งหมด]package main;import (
"fmt"
"time"
);
func buyGlassesAtSevenEleven() {
time.Sleep(1 * time.Second);
fmt.Println("ซื้อแว่น : ที่เซเว่น : เสร็จแล้ว");
}
func buyWatchAtCentral() {
time.Sleep(1 * time.Second);
fmt.Println("ซื้อนาฬิกา : ที่เซ็นทรัล : เสร็จแล้ว");
}
func buyFruitAtSiamParagon() {
time.Sleep(1 * time.Second);
fmt.Println("ซื้อผลไม้ : ที่สยามพารากอน : เสร็จแล้ว");
}
func buyCarAtToyota() {
time.Sleep(1 * time.Second);
fmt.Println("ซื้อรถ : ที่ศุนย์โตโยต้า : เสร็จแล้ว");
}
func sendToMisterA(message chan<- string) {//สร้าง Function (1)
time.Sleep(1 * time.Second);
message <- "กำลังส่งของให้ นาย A"; // นำค่าเข้าท่อ Channel (2)
}
func main() { start := time.Now(); // เริ่มจับเวลาตอนโปรแกรมทำงาน (3) ch := make(chan string); // สร้างท่อ Channel เอาไว้ส่งข้อมูล (4) go buyGlassesAtSevenEleven();
go buyWatchAtCentral();
go sendToMisterA(ch); // ส่งท่อ ch เข้าไปใน Function (5) buyFruitAtSiamParagon();
buyCarAtToyota();
messageFromMisterB := <-ch; // ค่าจากท่อ Channel จะออกตรงนี้ (6) if messageFromMisterB == "กำลังส่งของให้ นาย A" { // (7)
fmt.Println("นาย A ได้รับของแล้ว");
fmt.Println("ใช้เวลาในการ Run ทั้งสิ้น : ",time.Since(start),"วินาที");
};
}
[ผลลัพธ์ที่ได้จากการ Run main.go]
ซื้อแว่น : ที่เซเว่น : เสร็จแล้ว
ซื้อผลไม้ : ที่สยามพารากอน : เสร็จแล้ว
ซื้อนาฬิกา : ที่เซ็นทรัล : เสร็จแล้ว
ซื้อรถ : ที่ศุนย์โตโยต้า : เสร็จแล้ว
นาย A ได้รับของแล้ว
ใช้เวลาในการ Run ทั้งสิ้น : 2s วินาที

อธิบาย Source Code

(1) สร้าง Function sendToMisterA() โดยจะรับ Parameter ที่เป็น Channel String

(2) ก่อนจะเสร็จ Function sendToMisterA() ให้ส่งนำข้อความว่า “กำลังส่งของให้ นาย A” ใส่ท่อ Channel ไปด้วย

(3) สั่งให้โปรแกรมจับเวลา เพื่อที่เราจะได้รู้ว่าโปรแกรมเราใช้เวลา Run ไปเท่าไหร่

(4) สร้างท่อ Channel ขึ้นมา 1 ท่อ

(5) ส่งท่อเข้าไปยัง Function ที่ใช้ Go Routines เพื่อเชื่อมท่อให้ Function sendToMisterA() ที่ทำงานผ่าน Go Routines สามารถ ส่งค่าต่างๆมายัง ตัว Main โปรแกรมหลักได้ เมื่อ Function sendToMisterA() ทำงานเสร็จแล้ว

(6) เมื่อ Function sendToMisterA() ทำงานเสร็จแล้ว จะส่งค่าผ่านท่อ Channel มาให้ตัวแปร ที่ชื่อว่า messageFromMisterB และบรรทัดนี้จะยังไม่ทำงานต่อ จนกว่า Function sendToMisterA() จะทำงานเสร็จด้วยนะ มันจะรอจนกว่าจะเสร็จ

(7) เมื่อ ตัวแปร messageFromMisterB ได้รับค่าจากท่อ Channel แล้ว ก็มา Check อีกทีว่าเป็นข้อความ “กำลังส่งของให้ นาย A” หรือเปล่า ถ้าใช่ก็ให้บอกว่า นาย A ได้รับของแล้ว และก็แสดงเวลาที่โปรแกรมใช้ทำงาน

เพื่อให้เห็นภาพมากยิ่งขึ้น เปิดเว็บ The Go Playground แล้ว ลอง Run ดูเลยครับ

เมื่อ Run แล้วจะได้ผลลัพธ์แบบนี้

เวลา Run แล้วจะได้ผลลัพธ์แบบนี้

สรุป Channel คือท่อ ท่อนึงที่ทำให้ Go Routine สามารถส่ง ค่าออกมาจากการทำงานเบื้องหลังได้

ในบทต่อไป ตอนที่ 7 เป็นเรื่องที่สำคัญมากๆ สำหรับการเขียนโปรแกรม ก็คือเรื่อง Unit Test ด้วย ภาษา Go ว่าเราจะเริ่มเขียน Unit Test กันยังไงนะ รอติดตามกันนะครับ ฮึบ

--

--