การแนะนำ Go ants
ants
เป็นพื้นที่ goroutine ที่มีประสิทธิภาพสูง ซึ่งดำเนินการตารางเวลาและการจัดการกับ goroutine จำนวนมาก ทำให้สามารถ จำกัดและนำกลับใช้ทรัพยากร และให้การแบบมีประสิทธิภาพมากขึ้นในการดำเนินโปรแกรมพร้อมกัน
คุณสมบัติ
- จัดตารางเวลาอัตโนมัติสำหรับ goroutine จำนวนมากและนำกลับใช้
- ทำความสะอาด goroutine ที่หมดอายุเป็นประจำ เพื่อประหยัดทรัพยากร
- ให้อินเตอร์เฟซที่มีประโยชน์มากมาย: การส่งงาน, การรับค่าจำนวน goroutine ที่กำลังทำงาน, การปรับขนาดพื้นที่โดยเส้น, การปล่อยพื้นที่, และการเริ่มต้นพื้นที่ใหม่
- จัดการ panics อย่างละเอียดเพื่อป้องกันการล่มของโปรแกรม
- การนำกลับใช้ทรัพยากร ช่วยทำให้ใช้หน่วยความจำน้อยลงอย่างมาก ในสถานการณ์ของงานพร้อมกันแบบมวลเป็นอันดับสูงมีประสิทธิภาพมากกว่าการใช้ goroutine แบบธรรมดา
- กลไกทำงานที่ไม่บล็อก
ants
ทำงานอย่างไร
แผนภาพการไหล
ภาพเคลื่อนไหว
การติดตั้ง
การใช้ ants
เวอร์ชัน v1:
go get -u github.com/panjf2000/ants
การใช้ ants
เวอร์ชัน v2 (เปิดใช้ GO111MODULE=on):
go get -u github.com/panjf2000/ants/v2
วิธีใช้
เมื่อเขียนโปรแกรม Go ที่ทำงานพร้อมกันที่เปิดใช้ goroutine จำนวนมาก มันจะใช้ทรัพยากรระบบ (หน่วยความจำ, CPU) ไปเป็นส่วนใหญ่ โดยการใช้ ants
คุณสามารถสร้างพื้นที่ goroutine เพื่อนำกลับใช้ goroutine และประหยัดทรัพยากร และปรับปรองประสิทธิภาพ:
package main
import (
"fmt"
"sync"
"sync/atomic"
"time"
"github.com/panjf2000/ants/v2"
)
var sum int32
func myFunc(i interface{}) {
n := i.(int32)
atomic.AddInt32(&sum, n)
fmt.Printf("run with %d\n", n)
}
func demoFunc() {
time.Sleep(10 * time.Millisecond)
fmt.Println("Hello World!")
}
func main() {
defer ants.Release()
runTimes := 1000
// ใช้พื้นที่ทั่วไป
var wg sync.WaitGroup
syncCalculateSum := func() {
demoFunc()
wg.Done()
}
for i := 0; i < runTimes; i++ {
wg.Add(1)
_ = ants.Submit(syncCalculateSum)
}
wg.Wait()
fmt.Printf("running goroutines: %d\n", ants.Running())
fmt.Printf("finish all tasks.\n")
// ใช้พื้นที่พร้อมกับฟังก์ชัน
// ตั้งค่า 10 สำหรับความจุของพื้นที่โดยเส้น goroutine และ 1 วินาทีสำหรับระยะเวลาหมดอายุ
p, _ := ants.NewPoolWithFunc(10, func(i interface{}) {
myFunc(i)
wg.Done()
})
defer p.Release()
// ส่งงานทีละอัน
for i := 0; i < runTimes; i++ {
wg.Add(1)
_ = p.Invoke(int32(i))
}
wg.Wait()
fmt.Printf("running goroutines: %d\n", p.Running())
fmt.Printf("finish all tasks, result is %d\n", sum)
}
การกำหนดค่าของพูล
// Option แทนฟังก์ชันทางเลือก
type Option func(opts *Options)
// Options ประกอบด้วยตัวเลือกทั้งหมดที่จะถูกใช้เมื่อเริ่มต้นพูล ants
type Options struct {
// ExpiryDuration เป็นช่วงเวลาสำหรับก้าวที่ต้องการทำความสะอาดทำหลาย
// สำหรับโกอรัทีนสกางเจอร์สแตกต่างกับที่มีการใช้ออกไปเมื่อมากกว่า ExpiryDuration
ExpiryDuration time.Duration
// PreAlloc บ่งบอกว่าจะทำการจัดสรรหน่วยความจำล่วงหน้าเมื่อเริ่มต้น Pool
PreAlloc bool
// จำนวนสูงสุดของ goroutine ที่บล็อกบน pool.Submit
// 0 (ค่าเริ่มต้น) หมายถึงไม่มีข้อจำกัดเช่นนั้น
MaxBlockingTasks int
// เมื่อ Nonblocking เป็นจริง Pool.Submit จะไม่ถูกบล็อก
// จะคืน ErrPoolOverload เมื่อ Pool.Submit ไม่สามารถทำได้ทันที
// เมื่อ Nonblocking เป็นจริง MaxBlockingTasks เป็นที่ไม่มีผล
Nonblocking bool
// PanicHandler ใช้ในการจัดการกับข้อผิดพลาดจาก goroutine แต่ละตัว
// ถ้าเป็น nil ข้อผิดพลาดจะถูกส่งออกอีกครั้งจาก goroutine ของของงาน
PanicHandler func(interface{})
// Logger เป็นโลโก้ที่กำหนดเองสำหรับการบันทึกข้อมูล หากไม่ได้ตั้งค่า,
// โลโก้มาตรฐานที่กำหนดโดยค่าเริ่มต้นจากแพ็คเกจ log จะถูกใช้
Logger Logger
}
// WithOptions ยอมรับการกำหนดค่าตั้งต้นทั้งหมด
func WithOptions(options Options) Option {
return func(opts *Options) {
*opts = options
}
}
// WithExpiryDuration ตั้งค่าเวลาช่วงทำความสะอาดระหว่าง goroutines
func WithExpiryDuration(expiryDuration time.Duration) Option {
return func(opts *Options) {
opts.ExpiryDuration = expiryDuration
}
}
// WithPreAlloc บ่งชี้ว่าควรทำการจัดสรรหน่วยความจำสำหรับ workers
func WithPreAlloc(preAlloc bool) Option {
return func(opts *Options) {
opts.PreAlloc = preAlloc
}
}
// WithMaxBlockingTasks ตั้งค่าจำนวนสูงสุดของ workers ที่ถูกบล็อกเมื่อมันมาสู่ความจุของ pool
func WithMaxBlockingTasks(maxBlockingTasks int) Option {
return func(opts *Options) {
opts.MaxBlockingTasks = maxBlockingTasks
}
}
// WithNonblocking บ่งบอกว่า pool จะคืนค่า nil เมื่อไม่มี workers ที่พร้อมใช้งาน
func WithNonblocking(nonblocking bool) Option {
return func(opts *Options) {
opts.Nonblocking = nonblocking
}
}
// WithPanicHandler ตั้งค่า panic handler
func WithPanicHandler(panicHandler func(interface{})) Option {
return func(opts *Options) {
opts.PanicHandler = panicHandler
}
}
// WithLogger ตั้งค่าโลโก้ที่กำหนดเอง
func WithLogger(logger Logger) Option {
return func(opts *Options) {
opts.Logger = logger
}
}
โดยใช้ฟังก์ชันเลือกทางเลือกต่าง ๆ เมื่อเรียกใช้ NewPool
/NewPoolWithFunc
, ค่าของแต่ละรายการการกำหนดค่าใน ants.Options
สามารถถูกตั้งค่าและใช้เพื่อปรับแต่งพูล goroutine ได้
พูลที่กำหนดเอง
ants
รองรับการสร้าง Pool ของผู้ใช้ที่มีความจุพูลเฉพาะ; ด้วยการเรียกใช้ NewPool
สามารถสร้าง Pool ใหม่ด้วยความจุที่ระบุได้ตามนี้:
p, _ := ants.NewPool(10000)
การส่งงาน
งานถูกส่งผ่านการเรียกใช้ ants.Submit(func())
ฟังก์ชัน:
ants.Submit(func(){})
การปรับความจุของพูล goroutine ได้เอง
เพื่อปรับความจุของพูล goroutine ได้เอง, คุณสามารถใช้ Tune(int)
ฟังก์ชันได้:
pool.Tune(1000) // ปรับความจุเป็น 1000
pool.Tune(100000) // ปรับความจุเป็น 100000
ฟังก์ชันนี้มีการป้องกันการใช้แธร์ดเซฟ
การจัดสรรหน่วยความจำให้กับโครูทีนคิวล่วงหน้า
ants
ช่วยให้คุณจัดสรรหน่วยความจำสำหรับพูลทั้งหมดได้, ซึ่งสามารถเพิ่มประสิทธิภาพของพูล goroutine ในสถานการณ์บางกรณีเฉพาะ. เช่น, ในสถานการณ์ที่จำเป็นต้องใช้พูลที่มีความจุสุดสูงและแต่ละงานภายใน goroutine ต้องใช้เวลานาน, การจัดสรรหน่วยความจำสำหรับคิวของ goroutine จะลดการจัดสรรหน่วยความจำที่ไม่จำเป็น
// ants จะทำการจัดสรรหน่วยความจำทั้งความจุของพูลเมื่อคุณเรียกใช้ฟังก์ชันนี้
p, _ := ants.NewPool(100000, ants.WithPreAlloc(true))
ปล่อยพูล
pool.Release()
รีสตาร์ทพูล
// โดยการเรียกใช้เมธอด Reboot() คุณสามารถเปิดใช้งานพูลที่เคยถูกทำลายไว้และมันได้ถูกใช้ซ้ำ
pool.Reboot()
เกี่ยวกับลำดับการดำเนินงานของงาน
ants
ไม่รับประกันลำดับของการดำเนินงานของงาน และลำดับการดำเนินงานไม่จำเป็นต้องสอดคล้องกับลำดับการส่งซึ่งเนื่องจาก ants
ประมวลผลงานทั้งหมดที่ส่งเข้ามาอย่างสามัญ และงานจะถูกมอบหมายไปยังผู้ทำงานที่กำลังทำงานอย่างสามัญ ทำให้การดำเนินงานของงานเป็นอย่างพร้อมกันและไม่แน่นอน