// Package routineCtrlUtil
//
//	@description: 协程控制-用于海量循环中，限制并发的最大同时执行协程数
//	@author:
//	@revision history:
//	@create date: 2022-02-23 17:23:06
package routineCtrlUtil

import "sync"

// RoutineCtrl
//
//	@description: 控制同时运行的协程数
type RoutineCtrl struct {
	wg    *sync.WaitGroup
	chNum chan struct{}
}

// Run
//
//		@description: 循环内待执行的协程，封装在f函数参数内执行。f函数的最大并发运行数受New参数maxNum限制
//	    注意：*** 待执行函数引用外变变量，程序可能会出现不符合逻辑的结果(外部变量变化时，闭包函数获取到的是变化后的变量值) ***
//	    *** 闭包函数避坑说明 ***
//	    方式1)将待执行函数内使用的外部变量，用参数传入，可避免不符合逻辑的结果
//	    方式2)将待执行函数内使用的外部变量，使用一个临时局部变量将其值“固定”下来，闭包内使用临时局部变量（临时局部变量不会再被修改）
//	       示例：routine_ctrl_test.go -> TestRoutineCtrl
//
// parameter:
//
//	@receiver rtCtrl:
//	@f: 循环内待执行的协程
//
// return:
func (rtCtrl *RoutineCtrl) Run(f func(interface{}), arg interface{}) {
	rtCtrl.wg.Add(1)
	rtCtrl.chNum <- struct{}{}
	go func() {
		defer func() {
			<-rtCtrl.chNum
			rtCtrl.wg.Done()
			recover() // 异常捕获
		}()

		f(arg) // 调用实际函数体
	}()
}

// Wait
//
//	@description: 在循环外面等待所有协程执行结束后返回
//
// parameter:
//
//	@receiver rtCtrl:
//
// return:
func (rtCtrl *RoutineCtrl) Wait() {
	rtCtrl.wg.Wait()
}

// New
//
//	@description: 产生一个协程控制对象
//
// parameter:
//
//	@maxNum: 限制调用Run时的f函数的最大运行协程数
//
// return:
//
//	@*RoutineCtrl:
func New(maxNum int) *RoutineCtrl {
	return &RoutineCtrl{
		wg:    &sync.WaitGroup{},
		chNum: make(chan struct{}, maxNum),
	}
}
