ข้ามไปยังเนื้อหา

Goroutines Preview

JavaScript เป็น single-threaded ความ concurrent มาจาก event loop พอ async operation (network, I/O) ทำงานเสร็จ event loop ก็เอา callback เข้าคิวรอ ส่วน async/await เป็นแค่ syntactic sugar บน Promise ที่ทำให้โค้ด async อ่านเหมือน synchronous แต่สุดท้ายก็ยังมีแค่ thread เดียวที่รันโค้ด JavaScript ของเราได้ในแต่ละช่วงเวลา

Go ใช้แนวทางที่ต่างออกไปสิ้นเชิง: goroutine คือ lightweight thread ที่ concurrent จริง ๆ โดย Go runtime จะ schedule กระจายลงบน OS thread หลายตัว เครื่องทั่วไปรัน goroutine พร้อมกันได้เป็นล้านตัว

keyword go เปิด function ใน goroutine ใหม่ — execution context ที่เบาและถูก schedule แยกอิสระ คำสั่งนี้ return ทันที แล้ว goroutine ก็รัน concurrent ต่อไป

TypeScript
// TypeScript — async/await (event loop, single thread)
async function fetchUser(id: number): Promise<User> {
const res = await fetch("/users/" + id);
return res.json();
}
async function main() {
// These run sequentially by default:
const u1 = await fetchUser(1);
const u2 = await fetchUser(2);
// To run concurrently, use Promise.all:
const [u1, u2] = await Promise.all([fetchUser(1), fetchUser(2)]);
}
Go
// Go — goroutines (OS-thread-backed, truly parallel)
func fetchUser(id int) User {
// ... actual network call
return User{ID: id}
}
func main() {
// go keyword — launches fetchUser concurrently
go fetchUser(1) // fire and forget (result lost)
go fetchUser(2)
// To collect results, use channels or sync.WaitGroup
}

sync.WaitGroup คือวิธีที่ง่ายที่สุดในการรอ goroutine จำนวนที่รู้แน่นอนให้เสร็จก่อน main goroutine จะออก ให้นึกถึงเป็น counter ที่ block จนกว่าจะนับถึงศูนย์

TypeScript
// TypeScript — Promise.all to wait for all
const results = await Promise.all(
[1, 2, 3].map(id => fetchUser(id))
);
console.log(results);
Go
func processItem(id int, wg *sync.WaitGroup) {
defer wg.Done() // decrement when done
fmt.Printf("processing %d\n", id)
}
func main() {
var wg sync.WaitGroup
for i := 1; i <= 3; i++ {
wg.Add(1) // increment counter
go processItem(i, &wg) // launch goroutine
}
wg.Wait() // block until counter == 0
fmt.Println("all done")
}

Channel คือท่อส่งข้อมูลที่มี type สำหรับส่งค่าระหว่าง goroutine คุณ send ค่าด้วย <- และ receive ค่าด้วย <- Channel จะ synchronize ระหว่าง sender กับ receiver คือการ send จะ block จนกว่าจะมีคนรับ และการ receive ก็ block จนกว่าจะมีคนส่ง (สำหรับ unbuffered channel)

TypeScript
// TypeScript — no native channel primitive
// Closest approximation: a queue + event emitter, or a stream
import { Readable } from "stream";
// ... complex plumbing required
Go
func producer(ch chan<- int) {
for i := 0; i < 5; i++ {
ch <- i // send i into channel
}
close(ch) // signal no more values
}
func main() {
ch := make(chan int) // unbuffered channel
go producer(ch) // runs concurrently
for v := range ch { // receive until channel closed
fmt.Println("received:", v)
}
}
package main
import (
"fmt"
"sync"
)
func fetchData(id int, wg *sync.WaitGroup, results chan<- string) {
defer wg.Done()
// Simulate work
result := fmt.Sprintf("result from worker %d", id)
results <- result
}
func main() {
const numWorkers = 3
var wg sync.WaitGroup
results := make(chan string, numWorkers) // buffered channel
for i := 1; i <= numWorkers; i++ {
wg.Add(1)
go fetchData(i, &wg, results)
}
// Close channel when all goroutines finish
go func() {
wg.Wait()
close(results)
}()
// Collect results
for r := range results {
fmt.Println(r)
}
fmt.Println("all workers done")
}
จะเปิด goroutine ใน Go ได้อย่างไร?
goroutine ใหม่เริ่มต้นด้วย stack memory ประมาณเท่าไหร่?
sync.WaitGroup.Wait() ทำอะไร?
การ send ไปยัง unbuffered channel block จนกว่า: