|
@@ -15,7 +15,20 @@ type TaskControl struct {
|
|
log *logrus.Logger
|
|
log *logrus.Logger
|
|
oneCtxTimeOutSecond int
|
|
oneCtxTimeOutSecond int
|
|
bHold bool
|
|
bHold bool
|
|
- ctxFunc func(ctx context.Context, inData interface{}) error
|
|
|
|
|
|
+ released bool
|
|
|
|
+ // 传入的 func
|
|
|
|
+ ctxFunc func(ctx context.Context, inData interface{}) error
|
|
|
|
+ // 输入结构锁
|
|
|
|
+ inputDataMap map[int64]*TaskData
|
|
|
|
+ inputDataMapLock sync.RWMutex
|
|
|
|
+ // 结束锁
|
|
|
|
+ cancelMap map[int64]context.CancelFunc
|
|
|
|
+ cancelMapLock sync.RWMutex
|
|
|
|
+ // 执行情况, 0 是成功,1 是未执行,2 是错误或者超时
|
|
|
|
+ executeInfoMap map[int64]TaskState
|
|
|
|
+ executeInfoMapLock sync.RWMutex
|
|
|
|
+
|
|
|
|
+ commonLock sync.RWMutex
|
|
}
|
|
}
|
|
|
|
|
|
func NewTaskControl(pollName string, size int, oneCtxTimeOutSecond int, log *logrus.Logger) (*TaskControl, error) {
|
|
func NewTaskControl(pollName string, size int, oneCtxTimeOutSecond int, log *logrus.Logger) (*TaskControl, error) {
|
|
@@ -25,6 +38,9 @@ func NewTaskControl(pollName string, size int, oneCtxTimeOutSecond int, log *log
|
|
tc.pollName = pollName
|
|
tc.pollName = pollName
|
|
tc.oneCtxTimeOutSecond = oneCtxTimeOutSecond
|
|
tc.oneCtxTimeOutSecond = oneCtxTimeOutSecond
|
|
tc.log = log
|
|
tc.log = log
|
|
|
|
+ tc.inputDataMap = make(map[int64]*TaskData, 0)
|
|
|
|
+ tc.cancelMap = make(map[int64]context.CancelFunc, 0)
|
|
|
|
+ tc.executeInfoMap = make(map[int64]TaskState, 0)
|
|
tc.antPoolBase, err = ants.NewPoolWithFunc(size, func(inData interface{}) {
|
|
tc.antPoolBase, err = ants.NewPoolWithFunc(size, func(inData interface{}) {
|
|
tc.baseFuncHandler(inData)
|
|
tc.baseFuncHandler(inData)
|
|
})
|
|
})
|
|
@@ -41,28 +57,64 @@ func (tc *TaskControl) SetCtxProcessFunc(pf func(ctx context.Context, inData int
|
|
}
|
|
}
|
|
|
|
|
|
// Invoke 向 SetCtxProcessFunc 设置的 Func 中提交数据处理
|
|
// Invoke 向 SetCtxProcessFunc 设置的 Func 中提交数据处理
|
|
-func (tc *TaskControl) Invoke(inData InputData) error {
|
|
|
|
- tc.wgBase.Add(1)
|
|
|
|
- inData.Wg = &tc.wgBase
|
|
|
|
- tc.log.Debugln("Index:", inData.Index, "Invoke wg.Add()")
|
|
|
|
|
|
+func (tc *TaskControl) Invoke(inData *TaskData) error {
|
|
|
|
+
|
|
|
|
+ // 需要先记录有那些 ID 进来,然后再记录那些是完整执行的,以及出错执行的
|
|
|
|
+ tc.setExecuteStatus(inData.Index, NoExecute)
|
|
|
|
+
|
|
err := tc.antPoolBase.Invoke(inData)
|
|
err := tc.antPoolBase.Invoke(inData)
|
|
if err != nil {
|
|
if err != nil {
|
|
- // 如果这个执行有问题,那么就把 wg 的计数器减一
|
|
|
|
- tc.log.Debugln("Index:", inData.Index, "Invoke Error wg.Done()")
|
|
|
|
- tc.wgBase.Done()
|
|
|
|
|
|
+ tc.setTaskDataStatus(inData, Error)
|
|
|
|
+ tc.setExecuteStatus(inData.Index, Error)
|
|
|
|
+ return err
|
|
}
|
|
}
|
|
|
|
|
|
- return err
|
|
|
|
|
|
+ tc.log.Debugln("Index:", inData.Index, "Invoke inputDataMap Lock()")
|
|
|
|
+ tc.inputDataMapLock.Lock()
|
|
|
|
+ tc.inputDataMap[inData.Index] = inData
|
|
|
|
+ tc.inputDataMapLock.Unlock()
|
|
|
|
+ tc.log.Debugln("Index:", inData.Index, "Invoke inputDataMap UnLock()")
|
|
|
|
+
|
|
|
|
+ return nil
|
|
}
|
|
}
|
|
|
|
|
|
func (tc *TaskControl) baseFuncHandler(inData interface{}) {
|
|
func (tc *TaskControl) baseFuncHandler(inData interface{}) {
|
|
- data := inData.(InputData)
|
|
|
|
|
|
+
|
|
|
|
+ data := inData.(*TaskData)
|
|
|
|
+
|
|
defer func() {
|
|
defer func() {
|
|
tc.log.Debugln("Index:", data.Index, "baseFuncHandler wg.Done()")
|
|
tc.log.Debugln("Index:", data.Index, "baseFuncHandler wg.Done()")
|
|
- data.Wg.Done()
|
|
|
|
|
|
+ tc.wgBase.Done()
|
|
}()
|
|
}()
|
|
|
|
+
|
|
|
|
+ // 实际执行的时候
|
|
|
|
+ tc.wgBase.Add(1)
|
|
|
|
+ tc.log.Debugln("Index:", data.Index, "baseFuncHandler wg.Add()")
|
|
|
|
+
|
|
|
|
+ var ctx context.Context
|
|
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(tc.oneCtxTimeOutSecond)*time.Second)
|
|
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(tc.oneCtxTimeOutSecond)*time.Second)
|
|
- defer cancel()
|
|
|
|
|
|
+ defer func() {
|
|
|
|
+
|
|
|
|
+ // 那么对应的需要取消掉 map 中的记录
|
|
|
|
+ tc.cancelMapLock.Lock()
|
|
|
|
+ delete(tc.cancelMap, data.Index)
|
|
|
|
+ tc.cancelMapLock.Unlock()
|
|
|
|
+ cancel()
|
|
|
|
+ }()
|
|
|
|
+
|
|
|
|
+ // 如果已经执行 Release 则返回
|
|
|
|
+ tc.commonLock.RLock()
|
|
|
|
+ if tc.released == true {
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+ tc.commonLock.RUnlock()
|
|
|
|
+
|
|
|
|
+ // 记录 cancel
|
|
|
|
+ tc.log.Debugln("Index:", data.Index, "baseFuncHandler cancelMapLock Lock()")
|
|
|
|
+ tc.cancelMapLock.Lock()
|
|
|
|
+ tc.cancelMap[data.Index] = cancel
|
|
|
|
+ tc.cancelMapLock.Unlock()
|
|
|
|
+ tc.log.Debugln("Index:", data.Index, "baseFuncHandler cancelMapLock UnLock()")
|
|
|
|
|
|
done := make(chan error, 1)
|
|
done := make(chan error, 1)
|
|
panicChan := make(chan interface{}, 1)
|
|
panicChan := make(chan interface{}, 1)
|
|
@@ -79,37 +131,158 @@ func (tc *TaskControl) baseFuncHandler(inData interface{}) {
|
|
select {
|
|
select {
|
|
case err := <-done:
|
|
case err := <-done:
|
|
if err != nil {
|
|
if err != nil {
|
|
- tc.log.Errorln(tc.pollName, "Index:", data.Index, "NewPoolWithFunc done with Error", err.Error())
|
|
|
|
|
|
+
|
|
|
|
+ tc.setTaskDataStatus(data, Error)
|
|
|
|
+ tc.setExecuteStatus(data.Index, Error)
|
|
|
|
+ tc.log.Errorln("PollName:", tc.pollName, "Index:", data.Index, "NewPoolWithFunc done with Error", err.Error())
|
|
|
|
+ } else {
|
|
|
|
+ tc.setTaskDataStatus(data, Success)
|
|
|
|
+ tc.setExecuteStatus(data.Index, Success)
|
|
}
|
|
}
|
|
return
|
|
return
|
|
case p := <-panicChan:
|
|
case p := <-panicChan:
|
|
- tc.log.Errorln(tc.pollName, "Index:", data.Index, "NewPoolWithFunc got panic", p)
|
|
|
|
|
|
+
|
|
|
|
+ tc.setTaskDataStatus(data, Error)
|
|
|
|
+ tc.setExecuteStatus(data.Index, Error)
|
|
|
|
+ tc.log.Errorln("PollName:", tc.pollName, "Index:", data.Index, "NewPoolWithFunc got panic", p)
|
|
return
|
|
return
|
|
case <-ctx.Done():
|
|
case <-ctx.Done():
|
|
- tc.log.Errorln(tc.pollName, "Index:", data.Index, "NewPoolWithFunc got time out", ctx.Err())
|
|
|
|
|
|
+
|
|
|
|
+ tc.setTaskDataStatus(data, Error)
|
|
|
|
+ tc.setExecuteStatus(data.Index, Error)
|
|
|
|
+ tc.log.Errorln("PollName:", tc.pollName, "Index:", data.Index, "NewPoolWithFunc got time out", ctx.Err())
|
|
return
|
|
return
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// Hold 自身进行阻塞,如果你是使用 Web 服务器,那么应该无需使用该方法
|
|
// Hold 自身进行阻塞,如果你是使用 Web 服务器,那么应该无需使用该方法
|
|
func (tc *TaskControl) Hold() {
|
|
func (tc *TaskControl) Hold() {
|
|
|
|
+ tc.commonLock.Lock()
|
|
tc.bHold = true
|
|
tc.bHold = true
|
|
|
|
+ tc.commonLock.Unlock()
|
|
tc.wgBase.Add(1)
|
|
tc.wgBase.Add(1)
|
|
tc.log.Debugln("Hold wg.Add()")
|
|
tc.log.Debugln("Hold wg.Add()")
|
|
tc.wgBase.Wait()
|
|
tc.wgBase.Wait()
|
|
}
|
|
}
|
|
|
|
|
|
func (tc *TaskControl) Release() {
|
|
func (tc *TaskControl) Release() {
|
|
- if tc.bHold == true {
|
|
|
|
- tc.log.Debugln("Release wg.Done()")
|
|
|
|
- tc.wgBase.Done()
|
|
|
|
- }
|
|
|
|
|
|
+
|
|
|
|
+ tc.log.Debugln("Release Start")
|
|
|
|
+
|
|
|
|
+ tc.commonLock.Lock()
|
|
|
|
+ tc.released = true
|
|
|
|
+ tc.commonLock.Unlock()
|
|
|
|
+
|
|
tc.log.Debugln("Release.Release")
|
|
tc.log.Debugln("Release.Release")
|
|
tc.antPoolBase.Release()
|
|
tc.antPoolBase.Release()
|
|
|
|
+
|
|
|
|
+ tc.log.Debugln("Release cancel() Start")
|
|
|
|
+ // 统一 cancel cancel
|
|
|
|
+ tc.cancelMapLock.Lock()
|
|
|
|
+ for i, cancelFunc := range tc.cancelMap {
|
|
|
|
+ tc.log.Debugln("Release cancel() Index:", i)
|
|
|
|
+ cancelFunc()
|
|
|
|
+ }
|
|
|
|
+ tc.cancelMapLock.Unlock()
|
|
|
|
+
|
|
|
|
+ tc.log.Debugln("Release cancel() End")
|
|
|
|
+
|
|
|
|
+ var bHold bool
|
|
|
|
+ tc.commonLock.RLock()
|
|
|
|
+ bHold = tc.bHold
|
|
|
|
+ tc.commonLock.RUnlock()
|
|
|
|
+ if bHold == true {
|
|
|
|
+ tc.log.Debugln("Release Hold wg.Done()")
|
|
|
|
+ tc.wgBase.Done()
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ tc.log.Debugln("Release End")
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+func (tc *TaskControl) Reboot() {
|
|
|
|
+
|
|
|
|
+ var release bool
|
|
|
|
+ tc.commonLock.RLock()
|
|
|
|
+ release = tc.released
|
|
|
|
+ tc.commonLock.RUnlock()
|
|
|
|
+
|
|
|
|
+ if release == true {
|
|
|
|
+ // 如果被释放了,那么第一次 Invoke 的时候需要重启这个 pool
|
|
|
|
+ tc.antPoolBase.Reboot()
|
|
|
|
+ // 需要把缓存的 map 清理掉
|
|
|
|
+ tc.inputDataMapLock.Lock()
|
|
|
|
+ tc.inputDataMap = make(map[int64]*TaskData, 0)
|
|
|
|
+ tc.inputDataMapLock.Unlock()
|
|
|
|
+
|
|
|
|
+ tc.cancelMapLock.Lock()
|
|
|
|
+ tc.cancelMap = make(map[int64]context.CancelFunc, 0)
|
|
|
|
+ tc.cancelMapLock.Unlock()
|
|
|
|
+
|
|
|
|
+ tc.executeInfoMapLock.Lock()
|
|
|
|
+ tc.executeInfoMap = make(map[int64]TaskState, 0)
|
|
|
|
+ tc.executeInfoMapLock.Unlock()
|
|
|
|
+
|
|
|
|
+ tc.commonLock.Lock()
|
|
|
|
+ tc.released = false
|
|
|
|
+ tc.commonLock.Unlock()
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// GetExecuteInfo 获取 所有 Invoke 的执行情况,需要在 下一次 Invoke 拿走,否则会清空
|
|
|
|
+// 成功执行的、未执行的、执行错误(超时)的
|
|
|
|
+func (tc *TaskControl) GetExecuteInfo() ([]int64, []int64, []int64) {
|
|
|
|
+
|
|
|
|
+ successList := make([]int64, 0)
|
|
|
|
+ noExecuteList := make([]int64, 0)
|
|
|
|
+ errorList := make([]int64, 0)
|
|
|
|
+
|
|
|
|
+ tc.executeInfoMapLock.RLock()
|
|
|
|
+
|
|
|
|
+ for i, state := range tc.executeInfoMap {
|
|
|
|
+ if state == Success {
|
|
|
|
+ successList = append(successList, i)
|
|
|
|
+ } else if state == NoExecute {
|
|
|
|
+ noExecuteList = append(noExecuteList, i)
|
|
|
|
+ } else if state == Error {
|
|
|
|
+ errorList = append(errorList, i)
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ tc.executeInfoMapLock.RUnlock()
|
|
|
|
+
|
|
|
|
+ return successList, noExecuteList, errorList
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// GetResult 获取 TaskData 的反馈值,需要在 下一次 Invoke 拿走,否则会清空
|
|
|
|
+func (tc *TaskControl) GetResult(index int64) (bool, *TaskData) {
|
|
|
|
+ tc.inputDataMapLock.RLock()
|
|
|
|
+ value, found := tc.inputDataMap[index]
|
|
|
|
+ tc.inputDataMapLock.RUnlock()
|
|
|
|
+ return found, value
|
|
}
|
|
}
|
|
|
|
|
|
-type InputData struct {
|
|
|
|
|
|
+func (tc *TaskControl) setExecuteStatus(index int64, status TaskState) {
|
|
|
|
+ tc.executeInfoMapLock.Lock()
|
|
|
|
+ tc.executeInfoMap[index] = status
|
|
|
|
+ tc.executeInfoMapLock.Unlock()
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+func (tc *TaskControl) setTaskDataStatus(taskData *TaskData, status TaskState) {
|
|
|
|
+ tc.inputDataMapLock.Lock()
|
|
|
|
+ taskData.Status = status
|
|
|
|
+ tc.inputDataMapLock.Unlock()
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+type TaskData struct {
|
|
|
|
+ Index int64
|
|
|
|
+ Status TaskState // 执行情况, 0 是成功,1 是未执行,2 是错误或者超时
|
|
OneVideoFullPath string
|
|
OneVideoFullPath string
|
|
- Index int
|
|
|
|
- Wg *sync.WaitGroup
|
|
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+type TaskState int
|
|
|
|
+
|
|
|
|
+const (
|
|
|
|
+ Success TaskState = iota
|
|
|
|
+ NoExecute
|
|
|
|
+ Error
|
|
|
|
+)
|