Browse Source

修复 task_control 的bug

Signed-off-by: allan716 <[email protected]>
allan716 3 years ago
parent
commit
ce1cf38d1a
2 changed files with 260 additions and 298 deletions
  1. 29 44
      internal/pkg/task_control/task_control.go
  2. 231 254
      internal/pkg/task_control/task_control_test.go

+ 29 - 44
internal/pkg/task_control/task_control.go

@@ -14,33 +14,30 @@ type TaskControl struct {
 	wgBase              sync.WaitGroup
 	log                 *logrus.Logger
 	oneCtxTimeOutSecond int
-	bHold               bool
 	released            bool
 	// 传入的 func
 	ctxFunc func(ctx context.Context, inData interface{}) error
 	// 输入结构锁
-	inputDataMap     map[int64]*TaskData
+	inputDataMap     map[int]*TaskData
 	inputDataMapLock sync.Mutex
 	// 结束锁
-	cancelMap     map[int64]context.CancelFunc
+	cancelMap     map[int]context.CancelFunc
 	cancelMapLock sync.Mutex
 	// 执行情况, 0 是成功,1 是未执行,2 是错误或者超时
-	executeInfoMap     map[int64]TaskState
+	executeInfoMap     map[int]TaskState
 	executeInfoMapLock sync.Mutex
 
 	commonLock sync.Mutex
 }
 
-// NewTaskControl 这里有梗,按目前测试情况是,ants 设置的协程最大数量其实无法控制住,有可能会超出最大数量运行,所以有概率会失败
-// 特别是测试用例包含了提前结束的情况测试的时候(提前使用 Release),就会出现比期望的执行结果多出来的部分。
 func NewTaskControl(size int, log *logrus.Logger) (*TaskControl, error) {
 
 	var err error
 	tc := TaskControl{}
 	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.inputDataMap = make(map[int]*TaskData, 0)
+	tc.cancelMap = make(map[int]context.CancelFunc, 0)
+	tc.executeInfoMap = make(map[int]TaskState, 0)
 	tc.antPoolBase, err = ants.NewPoolWithFunc(size, func(inData interface{}) {
 		tc.baseFuncHandler(inData)
 	})
@@ -62,6 +59,10 @@ func (tc *TaskControl) SetCtxProcessFunc(pollName string, pf func(ctx context.Co
 // Invoke 向 SetCtxProcessFunc 设置的 Func 中提交数据处理
 func (tc *TaskControl) Invoke(inData *TaskData) error {
 
+	// 实际执行的时候
+	tc.wgBase.Add(1)
+	tc.log.Debugln("Index:", inData.Index, "baseFuncHandler wg.Add()")
+
 	// 需要先记录有那些 ID 进来,然后再记录那些是完整执行的,以及出错执行的
 	tc.setExecuteStatus(inData.Index, NoExecute)
 
@@ -69,6 +70,8 @@ func (tc *TaskControl) Invoke(inData *TaskData) error {
 	if err != nil {
 		tc.setTaskDataStatus(inData, Error)
 		tc.setExecuteStatus(inData.Index, Error)
+		tc.log.Debugln("Index:", inData.Index, "baseFuncHandler wg.Done()")
+		tc.wgBase.Done()
 		return err
 	}
 
@@ -85,6 +88,11 @@ func (tc *TaskControl) baseFuncHandler(inData interface{}) {
 
 	data := inData.(*TaskData)
 
+	defer func() {
+		tc.log.Debugln("Index:", data.Index, "baseFuncHandler wg.Done()")
+		tc.wgBase.Done()
+	}()
+
 	// 如果已经执行 Release 则返回
 	nowRelease := false
 	tc.commonLock.Lock()
@@ -96,13 +104,6 @@ func (tc *TaskControl) baseFuncHandler(inData interface{}) {
 		return
 	}
 
-	// 实际执行的时候
-	tc.wgBase.Add(1)
-	tc.log.Debugln("Index:", data.Index, "baseFuncHandler wg.Add()")
-
-	tc.log.Debugln("Index:", data.Index, "baseFuncHandler wg.Done()")
-	tc.wgBase.Done()
-
 	var ctx context.Context
 	ctx, cancel := context.WithTimeout(context.Background(), time.Duration(tc.oneCtxTimeOutSecond)*time.Second)
 	defer func() {
@@ -165,11 +166,7 @@ func (tc *TaskControl) baseFuncHandler(inData interface{}) {
 
 // Hold 自身进行阻塞,如果你是使用 Web 服务器,那么应该无需使用该方法
 func (tc *TaskControl) Hold() {
-	tc.commonLock.Lock()
-	tc.bHold = true
-	tc.commonLock.Unlock()
-	tc.wgBase.Add(1)
-	tc.log.Debugln("Hold wg.Add()")
+	tc.log.Debugln("Hold()")
 	tc.wgBase.Wait()
 }
 
@@ -196,20 +193,8 @@ func (tc *TaskControl) Release() {
 
 	tc.log.Debugln("Release cancel() End")
 
-	var bHold bool
-	tc.commonLock.Lock()
-	bHold = tc.bHold
-	tc.commonLock.Unlock()
-	if bHold == true {
-		tc.log.Debugln("Release Hold wg.Done()")
-		tc.wgBase.Done()
-
-		tc.commonLock.Lock()
-		tc.bHold = false
-		tc.commonLock.Unlock()
-	}
-
 	tc.log.Debugln("Release End")
+	tc.log.Debugln("-------------------------------")
 }
 
 func (tc *TaskControl) Reboot() {
@@ -224,15 +209,15 @@ func (tc *TaskControl) Reboot() {
 		tc.antPoolBase.Reboot()
 		// 需要把缓存的 map 清理掉
 		tc.inputDataMapLock.Lock()
-		tc.inputDataMap = make(map[int64]*TaskData, 0)
+		tc.inputDataMap = make(map[int]*TaskData, 0)
 		tc.inputDataMapLock.Unlock()
 
 		tc.cancelMapLock.Lock()
-		tc.cancelMap = make(map[int64]context.CancelFunc, 0)
+		tc.cancelMap = make(map[int]context.CancelFunc, 0)
 		tc.cancelMapLock.Unlock()
 
 		tc.executeInfoMapLock.Lock()
-		tc.executeInfoMap = make(map[int64]TaskState, 0)
+		tc.executeInfoMap = make(map[int]TaskState, 0)
 		tc.executeInfoMapLock.Unlock()
 
 		tc.commonLock.Lock()
@@ -243,11 +228,11 @@ func (tc *TaskControl) Reboot() {
 
 // GetExecuteInfo 获取 所有 Invoke 的执行情况,需要在 下一次 Invoke 拿走,否则会清空
 // 成功执行的、未执行的、执行错误(超时)的
-func (tc *TaskControl) GetExecuteInfo() ([]int64, []int64, []int64) {
+func (tc *TaskControl) GetExecuteInfo() ([]int, []int, []int) {
 
-	successList := make([]int64, 0)
-	noExecuteList := make([]int64, 0)
-	errorList := make([]int64, 0)
+	successList := make([]int, 0)
+	noExecuteList := make([]int, 0)
+	errorList := make([]int, 0)
 
 	tc.executeInfoMapLock.Lock()
 
@@ -267,14 +252,14 @@ func (tc *TaskControl) GetExecuteInfo() ([]int64, []int64, []int64) {
 }
 
 // GetResult 获取 TaskData 的反馈值,需要在 下一次 Invoke 拿走,否则会清空
-func (tc *TaskControl) GetResult(index int64) (bool, *TaskData) {
+func (tc *TaskControl) GetResult(index int) (bool, *TaskData) {
 	tc.inputDataMapLock.Lock()
 	value, found := tc.inputDataMap[index]
 	tc.inputDataMapLock.Unlock()
 	return found, value
 }
 
-func (tc *TaskControl) setExecuteStatus(index int64, status TaskState) {
+func (tc *TaskControl) setExecuteStatus(index int, status TaskState) {
 	tc.executeInfoMapLock.Lock()
 	tc.executeInfoMap[index] = status
 	tc.executeInfoMapLock.Unlock()
@@ -287,7 +272,7 @@ func (tc *TaskControl) setTaskDataStatus(taskData *TaskData, status TaskState) {
 }
 
 type TaskData struct {
-	Index  int64
+	Index  int
 	Status TaskState // 执行情况, 0 是成功,1 是未执行,2 是错误或者超时
 	DataEx interface{}
 }

+ 231 - 254
internal/pkg/task_control/task_control_test.go

@@ -6,6 +6,7 @@ import (
 	"github.com/allanpk716/ChineseSubFinder/internal/pkg/log_helper"
 	"github.com/sirupsen/logrus"
 	"golang.org/x/net/context"
+	"sync"
 	"testing"
 	"time"
 )
@@ -24,241 +25,228 @@ func TestTaskControl_Invoke(t *testing.T) {
 		{
 			name: "00", args: args{
 				TimeTester{PoolName: "00",
+					ConcurrentCount: 1,
+					JobCount:        5,
+					OneJobWaitTime:  1,
+					OneJobTimeOut:   2,
+				}},
+			successProcessCount: 5,
+		},
+		{
+			name: "01", args: args{
+				TimeTester{PoolName: "01",
+					ConcurrentCount: 2,
+					JobCount:        5,
+					OneJobWaitTime:  1,
+					OneJobTimeOut:   2,
+				}},
+			successProcessCount: 5,
+		},
+		{
+			name: "02", args: args{
+				TimeTester{PoolName: "02",
+					ConcurrentCount: 3,
+					JobCount:        5,
+					OneJobWaitTime:  1,
+					OneJobTimeOut:   2,
+				}},
+			successProcessCount: 5,
+		},
+		// 超时的情况
+		{
+			name: "03", args: args{
+				TimeTester{PoolName: "03",
 					ConcurrentCount:  2,
 					JobCount:         5,
 					TimeAfterRelease: 5,
-					OneJobWaitTime:   1,
-					OneJobTimeOut:    2,
-					SelfHold:         true,
+					OneJobWaitTime:   2,
+					OneJobTimeOut:    1,
 					NeedRelease:      true}},
-			successProcessCount: 5,
+			successProcessCount: 0,
 		},
 		{
-			name: "01", args: args{
-				TimeTester{PoolName: "01",
+			name: "04", args: args{
+				TimeTester{PoolName: "04",
 					ConcurrentCount:  2,
 					JobCount:         5,
 					TimeAfterRelease: 5,
-					OneJobWaitTime:   1,
-					OneJobTimeOut:    2,
-					SelfHold:         false,
+					OneJobWaitTime:   2,
+					OneJobTimeOut:    1,
 					NeedRelease:      false}},
-			successProcessCount: 5,
+			successProcessCount: 0,
 		},
 		{
-			name: "02", args: args{
-				TimeTester{PoolName: "02",
+			name: "05", args: args{
+				TimeTester{PoolName: "05",
 					ConcurrentCount:  2,
 					JobCount:         5,
 					TimeAfterRelease: 5,
-					OneJobWaitTime:   1,
-					OneJobTimeOut:    2,
-					SelfHold:         false,
+					OneJobWaitTime:   2,
+					OneJobTimeOut:    1,
+					NeedRelease:      true}},
+			successProcessCount: 0,
+		},
+		// 主动触发 painic
+		{
+			name: "06", args: args{
+				TimeTester{PoolName: "06",
+					ConcurrentCount:  2,
+					JobCount:         5,
+					TimeAfterRelease: 5,
+					OneJobWaitTime:   2,
+					OneJobTimeOut:    1,
+
+					NeedRelease: true,
+					WantPanic:   true}},
+			successProcessCount: 0,
+		},
+		{
+			name: "07", args: args{
+				TimeTester{PoolName: "07",
+					ConcurrentCount:  2,
+					JobCount:         5,
+					TimeAfterRelease: 5,
+					OneJobWaitTime:   2,
+					OneJobTimeOut:    1,
+
+					NeedRelease: false,
+					WantPanic:   true}},
+			successProcessCount: 0,
+		},
+		{
+			name: "08", args: args{
+				TimeTester{PoolName: "08",
+					ConcurrentCount:  2,
+					JobCount:         5,
+					TimeAfterRelease: 5,
+					OneJobWaitTime:   2,
+					OneJobTimeOut:    1,
+
+					NeedRelease: true,
+					WantPanic:   true}},
+			successProcessCount: 0,
+		},
+		// 部分超时
+		{
+			name: "09", args: args{
+				TimeTester{PoolName: "09",
+					ConcurrentCount:          2,
+					JobCount:                 5,
+					TimeAfterRelease:         5,
+					OneJobWaitTime:           2,
+					OneJobTimeOut:            3,
+					NeedRelease:              true,
+					IndexOverThanAddMoreTime: 2}},
+			successProcessCount: 3,
+		},
+		{
+			name: "10", args: args{
+				TimeTester{PoolName: "10",
+					ConcurrentCount:          2,
+					JobCount:                 5,
+					TimeAfterRelease:         5,
+					OneJobWaitTime:           2,
+					OneJobTimeOut:            3,
+					NeedRelease:              false,
+					IndexOverThanAddMoreTime: 2}},
+			successProcessCount: 3,
+		},
+		{
+			name: "11", args: args{
+				TimeTester{PoolName: "11",
+					ConcurrentCount:          2,
+					JobCount:                 5,
+					TimeAfterRelease:         5,
+					OneJobWaitTime:           2,
+					OneJobTimeOut:            3,
+					NeedRelease:              true,
+					IndexOverThanAddMoreTime: 3}},
+			successProcessCount: 4,
+		},
+		// 使用 Release 取消
+		{
+			name: "12", args: args{
+				TimeTester{PoolName: "12",
+					ConcurrentCount:  1,
+					JobCount:         5,
+					TimeAfterRelease: 2,
+					OneJobWaitTime:   3,
+					OneJobTimeOut:    4,
 					NeedRelease:      true}},
+			successProcessCount: 0,
+		},
+		{
+			name: "13", args: args{
+				TimeTester{PoolName: "13",
+					ConcurrentCount:  2,
+					JobCount:         5,
+					TimeAfterRelease: 2,
+					OneJobWaitTime:   3,
+					OneJobTimeOut:    4,
+
+					NeedRelease: true}},
+			successProcessCount: 0,
+		},
+		{
+			name: "14", args: args{
+				TimeTester{PoolName: "14",
+					ConcurrentCount:  2,
+					JobCount:         5,
+					TimeAfterRelease: 4,
+					OneJobWaitTime:   3,
+					OneJobTimeOut:    4,
+
+					NeedRelease: true}},
+			successProcessCount: 2,
+		},
+		{
+			name: "15", args: args{
+				TimeTester{PoolName: "15",
+					ConcurrentCount:  1,
+					JobCount:         5,
+					TimeAfterRelease: 3,
+					OneJobWaitTime:   2,
+					OneJobTimeOut:    4,
+
+					NeedRelease: true}},
+			successProcessCount: 1,
+		},
+		{
+			name: "16", args: args{
+				TimeTester{PoolName: "16",
+					ConcurrentCount:  3,
+					JobCount:         5,
+					TimeAfterRelease: 4,
+					OneJobWaitTime:   3,
+					OneJobTimeOut:    4,
+
+					NeedRelease: true}},
+			successProcessCount: 3,
+		},
+		{
+			name: "17", args: args{
+				TimeTester{PoolName: "17",
+					ConcurrentCount:  4,
+					JobCount:         5,
+					TimeAfterRelease: 4,
+					OneJobWaitTime:   3,
+					OneJobTimeOut:    4,
+
+					NeedRelease: true}},
+			successProcessCount: 4,
+		},
+		{
+			name: "18", args: args{
+				TimeTester{PoolName: "18",
+					ConcurrentCount:  5,
+					JobCount:         5,
+					TimeAfterRelease: 4,
+					OneJobWaitTime:   3,
+					OneJobTimeOut:    4,
+
+					NeedRelease: true}},
 			successProcessCount: 5,
 		},
-		//// 超时的情况
-		//{
-		//	name: "03", args: args{
-		//		TimeTester{PoolName: "03",
-		//			ConcurrentCount:  2,
-		//			JobCount:         5,
-		//			TimeAfterRelease: 5,
-		//			OneJobWaitTime:   2,
-		//			OneJobTimeOut:    1,
-		//			SelfHold:         true,
-		//			NeedRelease:      true}},
-		//	successProcessCount: 0,
-		//},
-		//{
-		//	name: "04", args: args{
-		//		TimeTester{PoolName: "04",
-		//			ConcurrentCount:  2,
-		//			JobCount:         5,
-		//			TimeAfterRelease: 5,
-		//			OneJobWaitTime:   2,
-		//			OneJobTimeOut:    1,
-		//			SelfHold:         false,
-		//			NeedRelease:      false}},
-		//	successProcessCount: 0,
-		//},
-		//{
-		//	name: "05", args: args{
-		//		TimeTester{PoolName: "05",
-		//			ConcurrentCount:  2,
-		//			JobCount:         5,
-		//			TimeAfterRelease: 5,
-		//			OneJobWaitTime:   2,
-		//			OneJobTimeOut:    1,
-		//			SelfHold:         false,
-		//			NeedRelease:      true}},
-		//	successProcessCount: 0,
-		//},
-		//// 主动触发 painic
-		//{
-		//	name: "06", args: args{
-		//		TimeTester{PoolName: "06",
-		//			ConcurrentCount:  2,
-		//			JobCount:         5,
-		//			TimeAfterRelease: 5,
-		//			OneJobWaitTime:   2,
-		//			OneJobTimeOut:    1,
-		//			SelfHold:         true,
-		//			NeedRelease:      true,
-		//			WantPanic:        true}},
-		//	successProcessCount: 0,
-		//},
-		//{
-		//	name: "07", args: args{
-		//		TimeTester{PoolName: "07",
-		//			ConcurrentCount:  2,
-		//			JobCount:         5,
-		//			TimeAfterRelease: 5,
-		//			OneJobWaitTime:   2,
-		//			OneJobTimeOut:    1,
-		//			SelfHold:         false,
-		//			NeedRelease:      false,
-		//			WantPanic:        true}},
-		//	successProcessCount: 0,
-		//},
-		//{
-		//	name: "08", args: args{
-		//		TimeTester{PoolName: "08",
-		//			ConcurrentCount:  2,
-		//			JobCount:         5,
-		//			TimeAfterRelease: 5,
-		//			OneJobWaitTime:   2,
-		//			OneJobTimeOut:    1,
-		//			SelfHold:         false,
-		//			NeedRelease:      true,
-		//			WantPanic:        true}},
-		//	successProcessCount: 0,
-		//},
-		//// 部分超时
-		//{
-		//	name: "09", args: args{
-		//		TimeTester{PoolName: "09",
-		//			ConcurrentCount:          2,
-		//			JobCount:                 5,
-		//			TimeAfterRelease:         5,
-		//			OneJobWaitTime:           2,
-		//			OneJobTimeOut:            3,
-		//			SelfHold:                 true,
-		//			NeedRelease:              true,
-		//			IndexOverThanAddMoreTime: 2}},
-		//	successProcessCount: 3,
-		//},
-		//{
-		//	name: "10", args: args{
-		//		TimeTester{PoolName: "10",
-		//			ConcurrentCount:          2,
-		//			JobCount:                 5,
-		//			TimeAfterRelease:         5,
-		//			OneJobWaitTime:           2,
-		//			OneJobTimeOut:            3,
-		//			SelfHold:                 false,
-		//			NeedRelease:              false,
-		//			IndexOverThanAddMoreTime: 2}},
-		//	successProcessCount: 3,
-		//},
-		//{
-		//	name: "11", args: args{
-		//		TimeTester{PoolName: "11",
-		//			ConcurrentCount:          2,
-		//			JobCount:                 5,
-		//			TimeAfterRelease:         5,
-		//			OneJobWaitTime:           2,
-		//			OneJobTimeOut:            3,
-		//			SelfHold:                 false,
-		//			NeedRelease:              true,
-		//			IndexOverThanAddMoreTime: 3}},
-		//	successProcessCount: 4,
-		//},
-		//// 使用 Release 取消
-		//{
-		//	name: "12", args: args{
-		//		TimeTester{PoolName: "12",
-		//			ConcurrentCount:  1,
-		//			JobCount:         5,
-		//			TimeAfterRelease: 2,
-		//			OneJobWaitTime:   3,
-		//			OneJobTimeOut:    4,
-		//			SelfHold:         true,
-		//			NeedRelease:      true}},
-		//	successProcessCount: 0,
-		//},
-		//{
-		//	name: "13", args: args{
-		//		TimeTester{PoolName: "13",
-		//			ConcurrentCount:  2,
-		//			JobCount:         5,
-		//			TimeAfterRelease: 2,
-		//			OneJobWaitTime:   3,
-		//			OneJobTimeOut:    4,
-		//			SelfHold:         true,
-		//			NeedRelease:      true}},
-		//	successProcessCount: 0,
-		//},
-		//{
-		//	name: "14", args: args{
-		//		TimeTester{PoolName: "14",
-		//			ConcurrentCount:  2,
-		//			JobCount:         5,
-		//			TimeAfterRelease: 4,
-		//			OneJobWaitTime:   3,
-		//			OneJobTimeOut:    4,
-		//			SelfHold:         true,
-		//			NeedRelease:      true}},
-		//	successProcessCount: 2,
-		//},
-		//{
-		//	name: "15", args: args{
-		//		TimeTester{PoolName: "15",
-		//			ConcurrentCount:  1,
-		//			JobCount:         5,
-		//			TimeAfterRelease: 3,
-		//			OneJobWaitTime:   2,
-		//			OneJobTimeOut:    4,
-		//			SelfHold:         true,
-		//			NeedRelease:      true}},
-		//	successProcessCount: 1,
-		//},
-		//{
-		//	name: "16", args: args{
-		//		TimeTester{PoolName: "16",
-		//			ConcurrentCount:  3,
-		//			JobCount:         5,
-		//			TimeAfterRelease: 4,
-		//			OneJobWaitTime:   3,
-		//			OneJobTimeOut:    4,
-		//			SelfHold:         true,
-		//			NeedRelease:      true}},
-		//	successProcessCount: 3,
-		//},
-		//{
-		//	name: "17", args: args{
-		//		TimeTester{PoolName: "17",
-		//			ConcurrentCount:  4,
-		//			JobCount:         5,
-		//			TimeAfterRelease: 4,
-		//			OneJobWaitTime:   3,
-		//			OneJobTimeOut:    4,
-		//			SelfHold:         true,
-		//			NeedRelease:      true}},
-		//	successProcessCount: 4,
-		//},
-		//{
-		//	name: "18", args: args{
-		//		TimeTester{PoolName: "18",
-		//			ConcurrentCount:  5,
-		//			JobCount:         5,
-		//			TimeAfterRelease: 4,
-		//			OneJobWaitTime:   3,
-		//			OneJobTimeOut:    4,
-		//			SelfHold:         true,
-		//			NeedRelease:      true}},
-		//	successProcessCount: 5,
-		//},
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
@@ -274,7 +262,9 @@ func TestTaskControl_Invoke(t *testing.T) {
 	}
 }
 
-func process(name string, timeTester TimeTester) ([]int64, []int64, []int64, error) {
+func process(name string, timeTester TimeTester) ([]int, []int, []int, error) {
+
+	once := sync.Once{}
 
 	tc, err := NewTaskControl(timeTester.ConcurrentCount, log_helper.NewLogHelper(name, logrus.DebugLevel, time.Duration(7*24)*time.Hour, time.Duration(24)*time.Hour))
 	if err != nil {
@@ -283,49 +273,37 @@ func process(name string, timeTester TimeTester) ([]int64, []int64, []int64, err
 	tc.SetCtxProcessFunc(timeTester.PoolName, waitTimes, timeTester.OneJobTimeOut)
 
 	for i := 0; i < timeTester.JobCount; i++ {
-		go func(index int64) {
-			err := tc.Invoke(&TaskData{Index: index,
-				DataEx: DataEx{
-					OneJobWaitTime:           timeTester.OneJobWaitTime,
-					WantPanic:                timeTester.WantPanic,
-					IndexOverThanAddMoreTime: int64(timeTester.IndexOverThanAddMoreTime),
-				}})
-			if err != nil {
-				tc.log.Errorln("Index:", index, "Error", err)
-			}
-		}(int64(i))
-	}
 
-	go func() {
-		if timeTester.NeedRelease == false {
-			tc.log.Infoln("Do not need Release")
-			return
-		}
-		tc.log.Infoln("Release After", timeTester.TimeAfterRelease, "Second")
-		time.Sleep(time.Duration(timeTester.TimeAfterRelease) * time.Second)
-		tc.Release()
-	}()
+		once.Do(func() {
+			go func() {
+				if timeTester.NeedRelease == false {
+					tc.log.Infoln("Do not need Release")
+					return
+				}
+				tc.log.Infoln("Release After", timeTester.TimeAfterRelease, "Second")
+				time.Sleep(time.Duration(timeTester.TimeAfterRelease) * time.Second)
+				tc.Release()
+			}()
+		})
 
-	fmt.Println("-------------------------------")
-	if timeTester.SelfHold == true {
+		err := tc.Invoke(&TaskData{Index: i,
+			DataEx: DataEx{
+				OneJobWaitTime:           timeTester.OneJobWaitTime,
+				WantPanic:                timeTester.WantPanic,
+				IndexOverThanAddMoreTime: timeTester.IndexOverThanAddMoreTime,
+			}})
+		if err != nil {
+			tc.log.Errorln("Index:", i, "Error", err)
+		}
+	}
 
-		fmt.Println("Start Hold")
-		tc.Hold()
-		fmt.Println("End Hold")
-	} else {
+	tc.Hold()
 
-		waitTime := timeTester.JobCount * timeTester.OneJobWaitTime
-		fmt.Printf("wait %ds start\n", waitTime)
-		time.Sleep(time.Duration(waitTime) * time.Second)
-		fmt.Printf("wait %ds end\n", waitTime)
-	}
 	fmt.Println("-------------------------------")
 
 	// 获取提前终止的计数器以及完成的计数器
 	successList, noExecuteList, errorList := tc.GetExecuteInfo()
 
-	tc.Release()
-
 	return successList, noExecuteList, errorList, nil
 }
 
@@ -378,7 +356,6 @@ type TimeTester struct {
 	TimeAfterRelease         int    // 开始后等待多久执行 Release 操作
 	OneJobWaitTime           int    // 单个任务得耗时
 	OneJobTimeOut            int    // 单个任务的超时时间
-	SelfHold                 bool   // 是否需要自身的等待,如果使用了,那么一定需要 Release
 	NeedRelease              bool   // 是否需要主动执行 Release
 	WantPanic                bool   // 触发 panic
 	IndexOverThanAddMoreTime int    // waitTimes函数中某个 Index 之后都会在等待处理上多加延时以便触发超时逻辑
@@ -387,5 +364,5 @@ type TimeTester struct {
 type DataEx struct {
 	OneJobWaitTime           int
 	WantPanic                bool
-	IndexOverThanAddMoreTime int64
+	IndexOverThanAddMoreTime int
 }