กระจายการทำงานหลายๆ งาน ผ่านหลายๆ goroutine ง่ายๆ ด้วย package errgroup
บางการทำงานเช่นโหลดข้อมูลจากไฟล์หลายๆ record แล้วเขียนลง database เราไม่จำเป็นต้องให้มันทำงานทีละ 1 record เสร็จก่อนแล้วค่อยไปทำเขียน record ถัดไปก็ได้ เราสามารถแยกการทำงานให้ทำในหลายๆ goroutine แล้วใช้ความสามารถของเครื่อง (CPU) ให้เต็มที่ถ้ามีหลายๆ CPU หลายๆ core ซึ่ง package errgroup ช่วยเราทำแบบนี้ได้พร้อมจัดการ error และ cancel context ได้อีกด้วย
ตัวอย่างเช่นเรามีโค้ดที่เขียนข้อมูล users ลง database แบบนี้
for _, user := range users {
err := db.Insert(ctx, uesr)
if err != nil {
return err
}
}
การ Insert ก็จะทำทีละ user จบก่อนค่อยไป Insert user ถัดไป
เราสามารถเขา errgroup มาช่วยได้ โค้ดจะเป็นแบบนี้
grp, grpCtx := errgroup.WithContext(ctx) // (1)
for _, user := range users {
user := user
grp.Go(func() err { // (2)
return db.Insert(grpCtx, user)
})
}
if err := grp.Wait(); err != nil { // (3)
return err
}
โดยมีวิธีใช้งานแบบนี้
- เราสร้าง errgroup value ขึ้นมาก่อนโดยใช้ func
WithContext
ซึ่งจะรับ context เข้าไปด้วยแล้วสร้าง Group value กับ Group context กลับมาให้เรา - เราเอา grp ไปเรียกใช้ method
Go
ซึ่งจะรับค่าเป็น anonymous function ที่ต้อง return error กลับออกมา ซึ่งฟังก์ชันที่เราส่งไปให้Go
เนี่ยแหละคือสิ่งที่เราอยากให้มันทำงานแบบ concurrent เช่นตามตัวอย่างคือ inesrt user เข้าไป, ในกรณีนี้เราต้องเปลี่ยน context ที่เคยส่งในตอนเรียก Insert เป็น group context (grpCtx) แทนด้วย เวลาเกิดมีข้อผิดพลาดในการทำงานสัก 1 ครั้งจะได้ยกเลิกการทำงานของการ insert ที่เหลือไปด้วยเลย - เรียก method
Wait()
เพื่อรอให้การทำงานของทุกๆ goroutine ที่ methodGo
สร้างไว้ทำงานเสร็จ แล้วก็จะส่งค่า error กลับมา ถ้าไม่มี error ก็ได้ค่า nil ถ้ามีก็ได้ค่า error ค่าแรกที่เกิดขึ้นนั่นเอง
เท่านี้เราก็ได้การทำงานแบบ concurrent ง่ายๆ ที่สามารถจัดการ context และ error ให้เราได้ด้วย ได้แล้ว