การแนะนำ Go ants

ants เป็นพื้นที่ goroutine ที่มีประสิทธิภาพสูง ซึ่งดำเนินการตารางเวลาและการจัดการกับ goroutine จำนวนมาก ทำให้สามารถ จำกัดและนำกลับใช้ทรัพยากร และให้การแบบมีประสิทธิภาพมากขึ้นในการดำเนินโปรแกรมพร้อมกัน

คุณสมบัติ

  • จัดตารางเวลาอัตโนมัติสำหรับ goroutine จำนวนมากและนำกลับใช้
  • ทำความสะอาด goroutine ที่หมดอายุเป็นประจำ เพื่อประหยัดทรัพยากร
  • ให้อินเตอร์เฟซที่มีประโยชน์มากมาย: การส่งงาน, การรับค่าจำนวน goroutine ที่กำลังทำงาน, การปรับขนาดพื้นที่โดยเส้น, การปล่อยพื้นที่, และการเริ่มต้นพื้นที่ใหม่
  • จัดการ panics อย่างละเอียดเพื่อป้องกันการล่มของโปรแกรม
  • การนำกลับใช้ทรัพยากร ช่วยทำให้ใช้หน่วยความจำน้อยลงอย่างมาก ในสถานการณ์ของงานพร้อมกันแบบมวลเป็นอันดับสูงมีประสิทธิภาพมากกว่าการใช้ goroutine แบบธรรมดา
  • กลไกทำงานที่ไม่บล็อก

ants ทำงานอย่างไร

แผนภาพการไหล

ants-flowchart-cn

ภาพเคลื่อนไหว

การติดตั้ง

การใช้ 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 ประมวลผลงานทั้งหมดที่ส่งเข้ามาอย่างสามัญ และงานจะถูกมอบหมายไปยังผู้ทำงานที่กำลังทำงานอย่างสามัญ ทำให้การดำเนินงานของงานเป็นอย่างพร้อมกันและไม่แน่นอน