ทำความเข้าใจ Go Concurrency และ Goroutines สำหรับมือใหม่หัดเขียนโปรแกรม

9 นาที 15 views บันทึกเป็น PDF
ทำความเข้าใจ Go Concurrency และ Goroutines สำหรับมือใหม่หัดเขียนโปรแกรม

เรียนรู้พื้นฐาน Go Concurrency และการใช้ Goroutines เพื่อให้โปรแกรมทำงานหลายอย่างพร้อมกันได้แบบมือโปร พร้อมวิธีสื่อสารระหว่างงานด้วย Channels แบบเข้าใจง่าย

ทำความเข้าใจเรื่อง Concurrency ในแบบที่เห็นภาพชัด

เวลาเราเขียนโปรแกรมแบบปกติ คอมพิวเตอร์จะทำงานทีละอย่างตามลำดับจากบนลงล่าง ถ้าต้องรออะไรนานๆ เช่น รอโหลดข้อมูลจากเว็บไซต์อื่น โปรแกรมของเราก็จะค้างหรือหยุดนิ่งไปเลยจนกว่างานนั้นจะเสร็จ นี่คือสิ่งที่เรียกว่า Sequential Programming (การเขียนโปรแกรมแบบเป็นลำดับขั้น) ซึ่งอาจไม่ตอบโจทย์ในยุคที่ทุกอย่างต้องรวดเร็ว

Concurrency (การทำงานหลายอย่างสลับกันไปมาในเวลาเดียวกัน) คือทางออกที่ช่วยให้โปรแกรมเราฉลาดขึ้น ลองนึกภาพว่าคุณกำลังทำอาหาร ถ้าต้องรอให้น้ำเดือดแล้วค่อยเริ่มหั่นผัก คุณจะเสียเวลาเปล่า แต่ถ้าคุณหั่นผักไปพร้อมกับรอน้ำเดือด นั่นแหละคือแนวคิดของมัน โปรแกรมเมอร์จะใช้เทคนิคนี้เพื่อจัดการงานหลายอย่างโดยไม่ทำให้ผู้ใช้งานต้องรอคอยอย่างเสียเปล่า

ในภาษา Go เรามีเครื่องมือที่ทรงพลังมากชื่อว่า Goroutines (หน่วยประมวลผลขนาดเล็กที่ภาษา Go จัดการให้เอง) มันช่วยให้เราสั่งงานให้ทำงานแบบคู่ขนานได้ง่ายมาก โดยไม่ต้องกังวลเรื่องการจัดการทรัพยากรเครื่องที่ซับซ้อนเหมือนภาษาอื่น นี่คือหัวใจสำคัญที่ทำให้ Go เป็นภาษาโปรแกรมยอดนิยมในการสร้างระบบที่รองรับผู้ใช้งานจำนวนมาก

Goroutines: ก้าวแรกสู่การทำงานแบบคู่ขนาน

Goroutine คือวิธีรันฟังก์ชันให้ทำงานแยกออกมาจากโปรแกรมหลัก โดยเราแค่ใส่คำสั่ง go ไว้หน้าฟังก์ชันเท่านั้น โปรแกรมก็จะรันฟังก์ชันนั้นในพื้นหลังทันทีโดยไม่รอให้งานชิ้นนั้นเสร็จก่อนแล้วค่อยไปทำบรรทัดถัดไป นี่คือความง่ายที่ทำให้มือใหม่หลายคนประทับใจตั้งแต่ครั้งแรกที่ลองเขียน

ลองดูตัวอย่างพื้นฐานนี้เพื่อให้เห็นภาพชัดเจนขึ้น เราจะสร้างฟังก์ชันที่พิมพ์ข้อความออกมา แล้วเรียกมันแบบปกติเทียบกับการเรียกแบบ Goroutine สังเกตว่าถ้าเราไม่สั่งให้โปรแกรมรอ มันอาจจะจบการทำงานก่อนที่ Goroutine จะทำงานเสร็จเสียอีก เพราะโปรแกรมหลักไม่ได้สนใจว่า Goroutine จะทำอะไรต่อ

package main
import (
    "fmt"
    "time"
)

func sayHi() {
    fmt.Println("สวัสดีจาก Goroutine!")
}

func main() {
    // สั่งให้ sayHi ทำงานแยกออกมา
    go sayHi() 
    
    // รอเวลาเล็กน้อยเพื่อให้ Goroutine ทำงานเสร็จ
    time.Sleep(1 * time.Second) 
}

ในโค้ดนี้ go sayHi() คือการสั่งให้ฟังก์ชันทำงานในฐานะ Goroutine ส่วน time.Sleep คือการสั่งให้โปรแกรมหลักหยุดรอ 1 วินาที เพื่อให้ Goroutine มีเวลาพิมพ์ข้อความออกมา หากเราลบบรรทัด time.Sleep ออก โปรแกรมอาจปิดตัวลงทันทีโดยไม่แสดงผลอะไรเลย ผลลัพธ์ที่ได้คือข้อความ "สวัสดีจาก Goroutine!" จะถูกพิมพ์ออกมาทางหน้าจอ

มือใหม่มักพลาดเรื่องการจบโปรแกรมก่อนที่ Goroutine จะทำงานเสร็จ จำไว้เสมอว่า Goroutine ไม่ได้การันตีว่าจะทำงานจบเมื่อไหร่ เราต้องมีวิธีจัดการหรือรอคอยมันอย่างถูกต้อง ในโปรเจกต์จริงเราจะไม่ใช้ time.Sleep แบบนี้ แต่จะใช้เครื่องมืออื่นที่เหมาะสมกว่าซึ่งเราจะเรียนรู้กันในหัวข้อถัดไป

Channels: ทางเชื่อมส่งต่อข้อมูลระหว่างงาน

เมื่อ Goroutine ทำงานแยกกันแล้ว คำถามต่อมาคือพวกมันจะคุยกันได้อย่างไร เพราะถ้าแยกกันอยู่ต่างคนต่างทำ ข้อมูลก็คงไม่ถึงกัน Channels (ช่องทางสำหรับส่งข้อมูลระหว่าง Goroutine) จึงถูกสร้างมาเพื่อแก้ปัญหานี้ เปรียบเสมือนท่อส่งข้อมูลที่ช่วยให้ Goroutine ส่งค่าไปหากันได้อย่างปลอดภัย

การสร้าง Channel ทำได้ง่ายๆ โดยใช้คำสั่ง make(chan ประเภทข้อมูล) เมื่อมีท่อแล้ว เราสามารถส่งข้อมูลเข้าท่อด้วยเครื่องหมาย <- และรับข้อมูลออกมาด้วยเครื่องหมายเดียวกัน การทำแบบนี้ช่วยให้เราไม่ต้องกังวลเรื่องการแย่งกันเข้าถึงข้อมูล ซึ่งเป็นปัญหาใหญ่ในการเขียนโปรแกรมแบบคู่ขนานที่เรียกว่า Race Condition (สภาวะที่การทำงานผิดพลาดเพราะแย่งกันแก้ไขข้อมูล)

ลองดูตัวอย่างการส่งข้อความผ่าน Channel กันครับ เราจะสร้าง Goroutine เพื่อส่งข้อความเข้าไปในท่อ แล้วให้โปรแกรมหลักรับข้อความนั้นออกมาแสดงผล วิธีนี้เป็นวิธีมาตรฐานที่โปรแกรมเมอร์มืออาชีพใช้สื่อสารระหว่างงานที่ทำพร้อมกัน เพื่อให้ข้อมูลถูกส่งผ่านอย่างถูกต้องและเป็นระเบียบ

package main
import "fmt"

func main() {
    // สร้างท่อสำหรับส่งข้อความชนิดสตริง
    messages := make(chan string)

    // สั่งให้ฟังก์ชันทำงานเพื่อส่งข้อมูลเข้าท่อ
    go func() {
        messages <- "ส่งข้อมูลสำเร็จแล้ว!"
    }()

    // รับข้อมูลจากท่อแล้วเก็บไว้ในตัวแปร
    msg := <-messages
    fmt.Println(msg)
}

ในโค้ดนี้ make(chan string) คือการสร้างช่องทางสื่อสาร messages <- คือการยัดข้อมูลลงในท่อ และ <-messages คือการดึงข้อมูลออกมา โปรแกรมจะรอจนกว่าจะมีข้อมูลส่งมาในท่อโดยอัตโนมัติ ผลลัพธ์ที่ได้คือข้อความ "ส่งข้อมูลสำเร็จแล้ว!" จะถูกพิมพ์ออกมาทางหน้าจออย่างแม่นยำ

ข้อควรระวังคือถ้าไม่มีใครมารับข้อมูลจาก Channel หรือไม่มีใครส่งข้อมูลเข้ามา โปรแกรมจะค้างหรือเกิดข้อผิดพลาดที่เรียกว่า Deadlock (สถานการณ์ที่โปรแกรมหยุดชะงักเพราะรอคอยกันไปมาอย่างไม่มีที่สิ้นสุด) ดังนั้นการวางแผนว่าจะส่งและรับข้อมูลตอนไหนจึงเป็นเรื่องสำคัญมากสำหรับโปรแกรมเมอร์ทุกคน

Worker Pools: การจัดการงานจำนวนมากให้เป็นระบบ

ถ้าคุณต้องทำงาน 1,000 อย่างพร้อมกัน การเปิด Goroutine 1,000 อันในคราวเดียวอาจทำให้เครื่องทำงานหนักเกินไป เราจึงนิยมใช้ Worker Pool (กลุ่มของคนงานที่คอยหยิบงานในคิวมาทำ) เพื่อจำกัดจำนวนงานที่ทำพร้อมกันให้พอดีกับทรัพยากรที่มีอยู่

แนวคิดนี้เหมือนกับเคาน์เตอร์ธนาคารที่มีพนักงาน 3 คน แต่มีลูกค้า 10 คนรอคิวอยู่ แทนที่ลูกค้าทุกคนจะพุ่งเข้าหาพนักงานพร้อมกัน ลูกค้าจะเข้าคิวและเดินไปหาพนักงานเมื่อพนักงานคนไหนว่าง การทำแบบนี้ช่วยให้ระบบของเรามีความเสถียรและไม่ล่มเมื่อมีงานเข้ามาเยอะเกินไป

ในการเขียน Go เราจะใช้ Channel เป็นคิวงาน และใช้ Goroutine เป็นพนักงานที่คอยดึงงานจากคิวมาทำ นี่เป็นเทคนิคที่สำคัญมากสำหรับงานจำพวกการประมวลผลไฟล์จำนวนมาก การส่งอีเมล หรือการดึงข้อมูลจากหลายๆ เว็บไซต์พร้อมกัน ซึ่งเป็นงานที่โปรแกรมเมอร์สาย Backend (ระบบหลังบ้าน) ต้องเจอเป็นปกติ

Synchronization: หัวใจของการควบคุมความถูกต้อง

แม้ว่า Goroutine จะทำงานแยกกันได้ แต่บางครั้งเราก็ต้องรอให้ทุกอย่างเสร็จก่อนถึงจะทำงานขั้นตอนถัดไปได้ เราเรียกสิ่งนี้ว่า Synchronization (การทำให้งานหลายอย่างประสานเวลาให้ตรงกัน) เครื่องมือที่นิยมที่สุดคือ WaitGroup (ตัวนับจำนวนงานที่รอให้เสร็จ)

ถ้าคุณไม่มี WaitGroup คุณจะไม่มีทางรู้เลยว่า Goroutine ทั้ง 10 ตัวที่คุณสั่งไปนั้นทำงานเสร็จครบหรือยัง WaitGroup จะทำหน้าที่นับจำนวนงานที่ค้างอยู่ เมื่อ Goroutine ทำงานเสร็จก็จะแจ้งว่า "ฉันเสร็จแล้วนะ" เมื่อตัวนับลดลงจนถึงศูนย์ โปรแกรมหลักถึงจะดำเนินการต่อได้

การใช้ WaitGroup ช่วยให้โปรแกรมของเรามีความน่าเชื่อถือ คุณจะไม่ต้องเดาเวลาด้วย time.Sleep อีกต่อไป ซึ่งเป็นวิธีที่อันตรายและไม่เป็นมืออาชีพ การฝึกใช้เครื่องมือเหล่านี้ให้คล่องจะช่วยให้คุณก้าวข้ามจากมือใหม่ไปสู่การเป็นโปรแกรมเมอร์ที่เขียนโค้ดได้อย่างมีประสิทธิภาพและปลอดภัย

สรุป: เปลี่ยนวิธีคิดเพื่อสร้างแอปพลิเคชันที่ทรงพลัง

การเรียนรู้เรื่อง Concurrency ใน Go ไม่ใช่แค่การจำคำสั่ง go หรือ chan แต่คือการเปลี่ยนวิธีคิดจากการมองงานเป็นเส้นตรง ไปเป็นการมองงานเป็นชุดของหน่วยย่อยที่ทำงานร่วมกันได้ เมื่อคุณเข้าใจหลักการนี้ คุณจะสามารถสร้างระบบที่รองรับการใช้งานหนักๆ ได้เหมือนกับบริษัทระดับโลกที่ใช้ Go ในการจัดการข้อมูลมหาศาล

ลองนึกถึงโปรเจกต์ที่คุณกำลังทำอยู่ตอนนี้ ถ้าคุณมีงานที่ต้องทำซ้ำๆ เช่น การดึงข้อมูลจาก API หลายตัว ลองนำ Goroutine ไปประยุกต์ใช้ดูครับ เริ่มจากการทำสิ่งเล็กๆ เช่น เขียนโปรแกรมดึงข้อมูลจาก 3 เว็บไซต์พร้อมกันแทนที่จะดึงทีละอัน คุณจะเห็นความแตกต่างของความเร็วอย่างชัดเจนทันที

การเป็นโปรแกรมเมอร์ที่เก่งไม่ได้วัดกันที่จำนวนโค้ดที่เขียน แต่คือการเลือกใช้เครื่องมือที่เหมาะสมกับงานมากที่สุด Concurrency คือพลังพิเศษที่คุณมีในภาษา Go จงฝึกฝนและทำความเข้าใจมันให้ลึกซึ้ง เพราะทักษะนี้จะเป็นอาวุธสำคัญที่จะทำให้คุณโดดเด่นในตลาดงานสายพัฒนาซอฟต์แวร์อย่างแน่นอนครับ


ที่มา: Go Concurrency Explained: How Goroutines Can Change the Way You Build Applications — DEV Community

แชร์บทความ

Facebook X LINE

บทความที่เกี่ยวข้อง

จัดการเซิร์ฟเวอร์ผ่าน VS Code ให้ง่ายขึ้นด้วย Easy SSH พร้อมฟีเจอร์โหลดไฟล์ผ่านคลิกเดียว

จัดการเซิร์ฟเวอร์ผ่าน VS Code ให้ง่ายขึ้นด้วย Easy SSH พร้อมฟีเจอร์โหลดไฟล์ผ่านคลิกเดียว

เบื่อไหมที่ต้องสลับหน้าจอไปมาเพื่อจัดการเซิร์ฟเวอร์? มาลองใช้ Easy SSH ปลั๊กอิน VS Code ที่ช่วยให้คุณรีโมทผ่าน Terminal ได้สะดวก แถมโหลดไฟล์ได้ง่ายแค่กด Ctrl+click

ที่มา: DEV Community

5 hours ago 11 นาที
3 views
วิธีดึงข้อมูลราคาจาก Google Hotels ด้วย API สำหรับนักพัฒนา

วิธีดึงข้อมูลราคาจาก Google Hotels ด้วย API สำหรับนักพัฒนา

อยากทำแอปท่องเที่ยวแต่ดึงข้อมูลราคาจาก Google Hotels ไม่ได้? มาดูวิธีใช้ Apify Actor ช่วยดึงข้อมูลแบบอัตโนมัติด้วย Python ง่ายๆ ไม่ต้องกลัวเว็บพัง

ที่มา: DEV Community

9 hours ago 8 นาที
5 views
วิธีเช็กความพร้อมโปรเจกต์ก่อนปล่อยงานจริงด้วย ReleaseReady

วิธีเช็กความพร้อมโปรเจกต์ก่อนปล่อยงานจริงด้วย ReleaseReady

เคยไหม? โค้ดรันได้ในเครื่องแต่พอปล่อยจริงกลับพัง! มาดูวิธีตรวจสอบความพร้อมของโปรเจกต์ก่อนอัปขึ้น GitHub ด้วยเครื่องมือ ReleaseReady กัน

ที่มา: DEV Community

12 hours ago 9 นาที
5 views