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

Google ส่ง Gemini ลง Mac แบบ Native พร้อมตัวช่วย AI สุดล้ำ ยกระดับงานเดสก์ท็อป

Google ส่ง Gemini ลง Mac แบบ Native พร้อมตัวช่วย AI สุดล้ำ ยกระดับงานเดสก์ท็อป

Google เปิดตัว Gemini เวอร์ชัน Native บน Mac พร้อมฟีเจอร์ AI ล้ำสมัย ช่วยเพิ่มประสิทธิภาพการทำงาน ปลดล็อกความคิดสร้างสรรค์ และเชื่อมต่อข้อมูลส่วนตัวได้อย่างชาญฉลาด

By ทีมงาน devdog
เจาะลึก UEFA Champions League: สุดยอดความตื่นเต้นที่แฟนบอลทั่วโลกรอคอย

เจาะลึก UEFA Champions League: สุดยอดความตื่นเต้นที่แฟนบอลทั่วโลกรอคอย

เจาะลึก UEFA Champions League การแข่งขันระดับโลกที่แฟนบอลรอคอย พร้อมติดตามข่าวสารรอบโลกและการถ่ายทอดสดสุดพิเศษ ไม่พลาดทุกความมันส์!

By ทีมงาน devdog
Google อัปเกรด Chrome ครั้งใหญ่ เพิ่มฟีเจอร์ "Skills" ให้ AI จำคำสั่งโปรดของคุณ

Google อัปเกรด Chrome ครั้งใหญ่ เพิ่มฟีเจอร์ "Skills" ให้ AI จำคำสั่งโปรดของคุณ

อัปเกรด Chrome ด้วยฟีเจอร์ Skills ใหม่ ให้ AI จดจำและเรียกใช้คำสั่งโปรดของคุณได้ทันที ไม่ต้องพิมพ์ซ้ำ พร้อมเชื่อมต่อ Gemini ทั่วระบบ

By ทีมงาน devdog
CARTIER Santos-Dumont โฉมใหม่: เมื่อออบซิเดียนผสานตำนานนักบิน สู่ความงามเหนือกาลเวลา

CARTIER Santos-Dumont โฉมใหม่: เมื่อออบซิเดียนผสานตำนานนักบิน สู่ความงามเหนือกาลเวลา

คาร์เทียร์เปิดตัว Santos-Dumont หน้าปัดออบซิเดียน หินภูเขาไฟธรรมชาติผสานดีไซน์นักบินระดับตำนาน สะท้อนงานฝีมือร่วมสมัยและความหรูหรา

By ทีมงาน devdog