GoLang: โค้ด Concurrency แบบง่ายๆ ด้วย Goroutine และ Channel

สวัสดีครับทุกคน! วันนี้เรามาคุยเรื่อง GoLang กันบ้างดีกว่า คือหลายคนอาจจะยังไม่เคยลอง หรือเคยลองแล้วแต่ยังงงๆ ว่าไอ้ Goroutine กับ Channel ที่เค้าพูดถึงกันนักหนาเนี่ย มันดียังไง?

ยอมรับเลยว่าตอนแรกผมก็ไม่ได้อินอะไรกับ Go มากนะ แต่พอได้มาลองเขียนโค้ดที่มันต้องทำงานพร้อมๆ กัน (Concurrency) เยอะๆ อ่ะ โห... Go นี่ตอบโจทย์มากจริงๆ เพราะมันออกแบบมาให้ทำเรื่องพวกนี้ง่ายตั้งแต่แรกเลย ไม่ต้องมานั่งจัดการ Thread วุ่นวายเหมือนภาษาอื่น

Goroutine มันคืออะไร?

คิดง่ายๆ ว่า Goroutine มันก็คือ function ที่รันพร้อมๆ กันไปกับ main function ของเรานั่นแหละครับ แต่มันเบากว่า Thread ทั่วไปเยอะมากๆ เบาจนแบบ เราสร้างเป็นแสนๆ ตัวมันก็ยังไหวอะ! ไม่ต้องกลัวเครื่องจะค้าง

วิธีสร้าง Goroutine ก็โคตรง่าย แค่เติม go เข้าไปหน้า function call แค่นั้นเอง

package main

import (
    "fmt"
    "time"
)

func sayHello() {
    time.Sleep(2 * time.Second) // แกล้งทำเป็นว่าทำงานนานๆ หน่อย
    fmt.Println("สวัสดีครับจาก Goroutine!")
}

func main() {
    go sayHello() // เรียก function sayHello ให้รันเป็น Goroutine
    fmt.Println("โปรแกรมหลักรันต่อเลยนะ ไม่ได้รอ")
    time.Sleep(3 * time.Second) // รอให้ Goroutine ทำงานเสร็จก่อน ไม่งั้นโปรแกรมหลักจบไปก่อน
    fmt.Println("โปรแกรมหลักจบแล้ว.")
}

ถ้าลองรันโค้ดข้างบน จะเห็นว่า โปรแกรมหลักรันต่อเลยนะ ไม่ได้รอ จะโผล่มาก่อน สวัสดีครับจาก Goroutine! นั่นแหละครับคือการทำงานแบบ Concurrency!

ปัญหานิดหน่อย: สังเกตว่าผมต้องใส่ time.Sleep ไว้ใน main ด้วย ไม่งั้นโปรแกรมมันจะจบไปก่อนที่ sayHello จะทำงานเสร็จอะ เพราะ Goroutine มันไม่ได้บล็อค main ไง ทีนี้จะสื่อสารกันยังไงล่ะ? ก็ต้องใช้ Channel นี่แหละ

Channel คืออะไร?

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

package main

import (
    "fmt"
    "time"
)

func worker(id int, jobs <-chan int, results chan<- int) {
    for j := range jobs {
        fmt.Printf("Worker %d กำลังประมวลผลงาน %d\n", id, j)
        time.Sleep(time.Second) // แกล้งทำว่าใช้เวลาประมวลผล
        results <- j * 2       // ส่งผลลัพธ์กลับไป
    }
}

func main() {
    const numJobs = 5
    jobs := make(chan int, numJobs)    // สร้าง channel สำหรับงานที่จะส่ง
    results := make(chan int, numJobs) // สร้าง channel สำหรับผลลัพธ์

    // สร้าง Goroutine worker 3 ตัว
    for w := 1; w <= 3; w++ {
        go worker(w, jobs, results)
    }

    // ส่งงานไปให้ worker
    for j := 1; j <= numJobs; j++ {
        jobs <- j
    }
    close(jobs) // บอกว่าไม่มีงานเพิ่มแล้วนะ ปิด channel งาน

    // รอรับผลลัพธ์จาก worker
    for a := 1; a <= numJobs; a++ {
        <-results
    }
    // ไม่จำเป็นต้อง close(results) เพราะเดี๋ยวโปรแกรมก็จบละ
    fmt.Println("ทุกงานเสร็จสมบูรณ์!")
}

ในตัวอย่างนี้: * เราสร้าง jobs channel เพื่อส่งงาน (เลขจำนวนเต็ม) และ results channel เพื่อรับผลลัพธ์ * มี worker Goroutine 3 ตัว คอยรับงานจาก jobs และส่งผลลัพธ์ไปที่ results * <-chan int คือ channel ที่รับได้แค่ (receive-only) * chan<- int คือ channel ที่ส่งได้อย่างเดียว (send-only) * close(jobs) สำคัญมาก! ถ้าไม่ปิด jobs channel ตัว for j := range jobs ใน worker มันจะค้างรอไปเรื่อยๆ ครับ เพราะมันไม่รู้ว่างานหมดแล้วรึยัง

ข้อควรระวัง (ที่ผมเคยพลาดบ่อยๆ): * Deadlock: ถ้า channel ไม่มีใครส่ง ไม่มีใครรับ หรือรับไม่ครบ จบเลยครับ! โปรแกรมจะค้าง หรือ panic ว่า fatal error: all goroutines are asleep - deadlock! บ่อยมากที่ลืม close channel หรือลืมรับค่าจนครบอะ * Buffer Size: make(chan int, 5) คือการสร้าง channel ที่มี buffer 5 ช่อง ถ้าไม่ใส่ buffer หรือใส่ make(chan int) เฉยๆ มันจะเป็น unbuffered channel ที่ต้องมีคนรอรับทันทีที่ส่ง ไม่งั้นก็บล็อคเหมือนกัน อันนี้แล้วแต่ use case นะ แต่ถ้าใช้แล้วงงๆ ลองคิดถึง buffer ดูครับ

ส่วนตัวผมมองว่า Go นี่แหละคืออนาคตของงานที่ต้องการ Concurrency สูงๆ เพราะมันใช้ง่าย เรียนรู้ไม่ยาก แถม performance ก็ดีมากๆ ด้วย ถ้ามีโปรเจคที่ต้องทำอะไรพร้อมๆ กันเยอะๆ หรือสร้าง API ที่ต้องการความเร็ว Go เป็นตัวเลือกที่ไม่ควรมองข้ามเลยจริงๆ ครับ ลองเอาไปเล่นดูนะ!

Read more

ไอลีน กู: ตำนานนักสกีฟรีสไตล์ผู้พลิกโฉมวงการและความหมายของชัยชนะ

ไอลีน กู: ตำนานนักสกีฟรีสไตล์ผู้พลิกโฉมวงการและความหมายของชัยชนะ

เจาะลึกเรื่องราวของ Eileen Gu นักสกีฟรีสไตล์ผู้สร้างประวัติศาสตร์ในโอลิมปิก 2026 สถิติที่ไม่เคยมีมาก่อน ประเด็นถกเถียง และความแข็งแกร่งส่วนตัวที่ทำให้เธอก้าวสู่ระดับโลก

By ทีมงาน devdog
วันพระ: คู่มือฉบับสมบูรณ์สำหรับพุทธศาสนิกชนและผู้สนใจยุคใหม่

วันพระ: คู่มือฉบับสมบูรณ์สำหรับพุทธศาสนิกชนและผู้สนใจยุคใหม่

เจาะลึกวันพระและความสำคัญของวันมาฆบูชา 2569 ทั้งวันหยุดราชการ ธนาคาร กิจกรรมเวียนเทียนต้นไม้ และผลกระทบต่อบริการขนส่ง เตรียมตัววางแผนทำบุญและพักผ่อน

By ทีมงาน devdog
ถอดรหัสรักแท้: "บังมัดคลองตันต้นข้าว" เรื่องราวที่สะท้อนการให้อภัยและการเริ่มต้นใหม่

ถอดรหัสรักแท้: "บังมัดคลองตันต้นข้าว" เรื่องราวที่สะท้อนการให้อภัยและการเริ่มต้นใหม่

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

By ทีมงาน devdog
ไฮไลท์บอลไทยลีก 2: มหาสารคาม เอสบีที เอฟซี กับฟอร์มร้อนแรงสู่เส้นทางเพลย์ออฟ

ไฮไลท์บอลไทยลีก 2: มหาสารคาม เอสบีที เอฟซี กับฟอร์มร้อนแรงสู่เส้นทางเพลย์ออฟ

เจาะลึกไฮไลท์บอลไทยลีก 2 ของมหาสารคาม เอสบีที เอฟซี กับฟอร์มร้อนแรง ชัยชนะสำคัญจาก ชิตชนก และบทบาทโค้ชดุสิต สู่เส้นทางเพลย์ออฟที่น่าจับตา!

By ทีมงาน devdog