Goroutines และ Channels: Concurrent Programming
NPXVERSE Team•13 กรกฎาคม 2567•Concurrency
GolangGoroutinesChannelsConcurrency
Goroutines
Goroutines คือ lightweight threads ใน Go ที่ใช้สำหรับการทำงานแบบ concurrent
การสร้าง Goroutine
package main import ( "fmt" "time" ) func sayHello(name string) { for i := 0; i < 5; i++ { fmt.Printf("Hello %s! (%d)\n", name, i+1) time.Sleep(100 * time.Millisecond) } } func main() { // Run in goroutine go sayHello("Alice") go sayHello("Bob") // Wait for goroutines to complete time.Sleep(1 * time.Second) fmt.Println("Main function completed") }
WaitGroup สำหรับรอ Goroutines
package main import ( "fmt" "sync" "time" ) func worker(id int, wg *sync.WaitGroup) { defer wg.Done() // เรียกเมื่อ function จบ fmt.Printf("Worker %d starting\n", id) time.Sleep(time.Second) fmt.Printf("Worker %d done\n", id) } func main() { var wg sync.WaitGroup // Start 3 workers for i := 1; i <= 3; i++ { wg.Add(1) // เพิ่มจำนวน goroutine ที่รอ go worker(i, &wg) } wg.Wait() // รอให้ทุก goroutine เสร็จ fmt.Println("All workers completed") }
Channels
Channels เป็นวิธีการสื่อสารระหว่าง goroutines อย่างปลอดภัย
Basic Channel Operations
package main import "fmt" func main() { // สร้าง channel messages := make(chan string) // ส่งข้อมูลใน goroutine go func() { messages <- "Hello from goroutine!" }() // รับข้อมูลจาก channel msg := <-messages fmt.Println(msg) }
Buffered Channels
package main import "fmt" func main() { // Buffered channel with capacity 2 numbers := make(chan int, 2) // สามารถส่งข้อมูล 2 ครั้งโดยไม่ต้องรอ receiver numbers <- 1 numbers <- 2 fmt.Println(<-numbers) // 1 fmt.Println(<-numbers) // 2 }
Channel Directions
package main import "fmt" // Channel ที่ส่งได้อย่างเดียว func sendData(ch chan<- string) { ch <- "Data from sender" } // Channel ที่รับได้อย่างเดียว func receiveData(ch <-chan string) { msg := <-ch fmt.Println("Received:", msg) } func main() { ch := make(chan string) go sendData(ch) receiveData(ch) }
Select Statement
package main import ( "fmt" "time" ) func main() { ch1 := make(chan string) ch2 := make(chan string) go func() { time.Sleep(1 * time.Second) ch1 <- "Message from channel 1" }() go func() { time.Sleep(2 * time.Second) ch2 <- "Message from channel 2" }() // Select รอ channel ไหนก็ได้ที่พร้อม for i := 0; i < 2; i++ { select { case msg1 := <-ch1: fmt.Println("From ch1:", msg1) case msg2 := <-ch2: fmt.Println("From ch2:", msg2) } } }
Worker Pool Pattern
package main import ( "fmt" "sync" "time" ) type Job struct { ID int Name string } type Result struct { Job Job Output string } func worker(id int, jobs <-chan Job, results chan<- Result, wg *sync.WaitGroup) { defer wg.Done() for job := range jobs { // จำลองการทำงาน time.Sleep(100 * time.Millisecond) result := Result{ Job: job, Output: fmt.Sprintf("Worker %d processed job %d: %s", id, job.ID, job.Name), } results <- result } } func main() { const numWorkers = 3 const numJobs = 10 jobs := make(chan Job, numJobs) results := make(chan Result, numJobs) var wg sync.WaitGroup // Start workers for i := 1; i <= numWorkers; i++ { wg.Add(1) go worker(i, jobs, results, &wg) } // Send jobs for i := 1; i <= numJobs; i++ { jobs <- Job{ ID: i, Name: fmt.Sprintf("Task-%d", i), } } close(jobs) // Close results channel when all workers are done go func() { wg.Wait() close(results) }() // Collect results for result := range results { fmt.Println(result.Output) } }
Context สำหรับ Cancellation
package main import ( "context" "fmt" "time" ) func doWork(ctx context.Context, name string) { for { select { case <-ctx.Done(): fmt.Printf("%s: Context cancelled, stopping work\n", name) return default: fmt.Printf("%s: Working...\n", name) time.Sleep(500 * time.Millisecond) } } } func main() { ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) defer cancel() go doWork(ctx, "Worker-1") go doWork(ctx, "Worker-2") time.Sleep(3 * time.Second) fmt.Println("Main function completed") }
Goroutines และ Channels เป็นจุดแข็งของ Go ที่ทำให้การเขียนโปรแกรมแบบ concurrent เป็นเรื่องง่ายและปลอดภัย