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

Goroutines

ใน TypeScript คุณมาร์ค function เป็น async แล้ว await ผลลัพธ์ ทุกครั้งที่เจอ await function จะหยุดพักและคืน control ให้ event loop แต่ทุกอย่างยังรันบน thread เดียว:

async function processUser(id: number) {
const user = await fetchUser(id);
return user;
}
await processUser(1); // you must await or .then() to consume it

ใน Go คุณเติม keyword go หน้า function call ใดก็ได้ call นั้นจะถูก dispatch เป็น goroutine — หน่วยทำงานน้ำหนักเบาที่รันอิสระ จัดการโดย Go runtime ไม่ต้องมี async ที่ตัว function และไม่ต้อง await ตอนเรียก:

go processUser(1) // fires immediately in the background

จุดต่างที่สำคัญ: async function ของ JavaScript ยังรันบน thread เดียว แต่ goroutine รันแบบ parallel บน OS thread คนละตัวได้จริง

TypeScript
// TypeScript: async/await (single-threaded event loop)
async function greet(name: string): Promise<void> {
console.log("Hello from " + name);
}
// sequential
await greet("Alice");
await greet("Bob");
// concurrent (still one thread, interleaved via event loop)
await Promise.all([greet("Alice"), greet("Bob")]);
Go
// Go: goroutines (truly concurrent)
package main
import (
"fmt"
"sync"
)
func greet(name string, wg *sync.WaitGroup) {
defer wg.Done() // signal WaitGroup when done
fmt.Printf("Hello from %s\n", name)
}
func main() {
var wg sync.WaitGroup
names := []string{"Alice", "Bob", "Carol"}
for _, name := range names {
wg.Add(1) // register before launching
go greet(name, &wg) // launch goroutine
}
wg.Wait() // block until all goroutines finish
fmt.Println("All done!")
}

sync.WaitGroup เป็นเครื่องมือหลักของ Go สำหรับรอ goroutines กลุ่มหนึ่งให้เสร็จ ทำงานคล้าย Promise.all แต่ใช้ counter แทน array:

  1. wg.Add(n) — เพิ่ม counter ก่อน launch goroutine
  2. wg.Done() — ลด counter เมื่อ goroutine เสร็จ (เกือบทุกครั้งใช้คู่กับ defer)
  3. wg.Wait() — block จนกว่า counter จะเป็น 0

กฎ: เรียก wg.Add(1) ก่อน go f() เสมอ อย่าเรียกข้างใน goroutine เพราะ scheduler อาจไปถึง wg.Wait() ก่อนที่ goroutine จะเริ่มรันด้วยซ้ำ ทำให้ Wait คืนค่าทันทีทั้งที่งานยังค้างอยู่

กฎ “Add ก่อน go” พลาดง่าย Go 1.25 จึงเพิ่ม WaitGroup.Go ที่ทำ Add(1) launch goroutine และเรียก Done ให้เมื่อ function return — รูปทรงตอนนี้อ่านแทบเหมือน Promise.all บน list ของ async call:

TypeScript
// TypeScript
await Promise.all(urls.map(async (url) => {
await fetchOne(url);
}));
Go
// Go 1.25+
var wg sync.WaitGroup
for _, url := range urls {
wg.Go(func() { fetchOne(url) }) // Add(1) + go + Done, handled for you
}
wg.Wait()

โค้ดใหม่ให้ใช้ wg.Go ส่วน wg.Add(1) / defer wg.Done() ยังเจอในโค้ดเบสส่วนใหญ่ (และตัวอย่างบางส่วนด้านล่าง) และตอนนี้ go vet จะเตือนถ้าวาง Add ไว้ข้างใน goroutine

เหมือน immediately-invoked arrow function ใน JS คุณ launch goroutine แบบ anonymous inline ได้เลย:

TypeScript
// TypeScript: immediately invoked async
(async () => {
const result = await doSomething();
console.log(result);
})();
Go
// Go: immediately launched anonymous goroutine
go func() {
result := doSomething()
fmt.Println(result)
}() // trailing () invokes the function literal immediately
package main
import (
"fmt"
"sync"
)
func printSquare(n int, wg *sync.WaitGroup) {
defer wg.Done()
fmt.Printf("%d² = %d\n", n, n*n)
}
func main() {
var wg sync.WaitGroup
for i := 1; i <= 5; i++ {
wg.Add(1)
go printSquare(i, &wg)
}
wg.Wait()
fmt.Println("All squares printed")
}
คีย์เวิร์ดใดที่ใช้ launch goroutine ใน Go?
เมื่อไหร่ควรเรียก wg.Add(1)?
Goroutine stack เริ่มต้นมีขนาดประมาณเท่าไหร่?
method ใดของ WaitGroup ที่ใช้บอกว่า goroutine เสร็จแล้ว?