เข้าใจ Context ใน Go เพื่อจัดการ timeout และ cancellation

Sharing is caring!

Context ในภาษา Go เป็นเครื่องมือที่ทรงพลังที่ช่วยให้เราจัดการกับการควบคุมเวลา, การยกเลิก (cancellation), และการส่งข้อมูลข้าม goroutine ได้อย่างมีประสิทธิภาพ บทความนี้จะช่วยให้คุณเข้าใจ context อย่างลึกซึ้ง พร้อมทั้งยกตัวอย่าง code และกรณีใช้งานจริง

Context คืออะไร?

ในภาษา Go แพคเกจ context ถูกออกแบบมาเพื่อ:

  • ส่งสัญญาณยกเลิกการทำงาน (cancellation signal)
  • ควบคุม timeout ของการทำงาน
  • ส่งค่าระหว่าง goroutine ได้แบบ thread-safe

ประเภทของ Context

  1. context.Background(): ใช้เมื่อเริ่มต้น
  2. context.WithCancel(): สร้าง context ที่สามารถยกเลิกได้
  3. context.WithTimeout(): สร้าง context ที่ยกเลิกอัตโนมัติเมื่อถึงเวลา
  4. context.WithDeadline(): เหมือน timeout แต่กำหนดเวลาตายตัว

1. context.WithCancel ตัวอย่าง

func main() {
  ctx, cancel := context.WithCancel(context.Background())

  go func(ctx context.Context) {
    for {
      select {
      case <-ctx.Done():
        fmt.Println("Canceled")
        return
      default:
        fmt.Println("Working...")
        time.Sleep(500 * time.Millisecond)
      }
    }
  }(ctx)

  time.Sleep(2 * time.Second)
  cancel()
  time.Sleep(1 * time.Second)
}
    

2. context.WithTimeout ตัวอย่าง

func main() {
  ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
  defer cancel()

  select {
  case <-time.After(3 * time.Second):
    fmt.Println("Finished work")
  case <-ctx.Done():
    fmt.Println("Timeout:", ctx.Err())
  }
}
    

3. ใช้ Context กับ HTTP Request

กรณีเรียก API แล้วต้องหยุดเมื่อเกิน 3 วินาที

func fetchAPI(ctx context.Context, url string) error {
  req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
  if err != nil {
    return err
  }

  client := http.Client{}
  resp, err := client.Do(req)
  if err != nil {
    return err
  }
  defer resp.Body.Close()
  // handle response
  return nil
}

func main() {
  ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
  defer cancel()
  err := fetchAPI(ctx, "https://example.com")
  if err != nil {
    fmt.Println("Request failed:", err)
  }
}
    

Best Practices ในการใช้ Context

  • ไม่ควรเก็บ context ใน struct
  • ส่ง context เป็น argument แรกในฟังก์ชัน
  • เรียก cancel() เสมอหลังจาก WithTimeout/WithCancel
  • ใช้ context.TODO() แทน nil เมื่อยังไม่รู้ว่าจะใช้ context อะไร

ภาพแสดงการทำงานของ Context

Context Chain และการสืบทอด

Context ใน Go มีลักษณะเป็น parent-child chain หาก context ต้นทางถูก cancel ลูกทั้งหมดจะหยุดทันที

ctx := context.Background()
ctx1, cancel1 := context.WithCancel(ctx)
ctx2, cancel2 := context.WithTimeout(ctx1, 1*time.Second)
ctx3 := context.WithValue(ctx2, "user", "admin")

// เมื่อ cancel1() ถูกเรียก ctx2 และ ctx3 จะโดนยกเลิกด้วย
cancel1()
    

เมื่อต้องใช้ vs ไม่ต้องใช้ Context

ควรใช้ Context เมื่อ:

  • มี HTTP request ที่ควบคุมเวลา
  • มี long-running task หรือ background task
  • ต้องควบคุม pipeline หรือ queue งานแบบ async

ไม่ควรใช้ Context เพื่อ:

  • ส่งข้อมูลทั่วไป (ควรใช้ struct)
  • เก็บค่าที่ไม่เกี่ยวข้องกับเวลา

สรุป

การเข้าใจ Context เป็นสิ่งสำคัญสำหรับการพัฒนาแอปพลิเคชัน Go ที่ดี โดยเฉพาะระบบที่มี concurrency, HTTP API, หรือ background task หากใช้ context อย่างถูกต้อง ระบบจะสามารถควบคุมงานได้ยืดหยุ่น มีเสถียรภาพ และ resource ไม่รั่ว


คำค้น SEO: Go context, golang timeout, cancellation context Go, context best practice, context กับ HTTP request, context.Background กับ context.TODO ต่างกันยังไง

Leave a Reply

อีเมลของคุณจะไม่แสดงให้คนอื่นเห็น ช่องข้อมูลจำเป็นถูกทำเครื่องหมาย *