641 lines
16 KiB
Go
641 lines
16 KiB
Go
package vqdtask
|
||
|
||
import (
|
||
"context"
|
||
"easyvqd/internal/core/vqd"
|
||
"easyvqd/pkg/vqdcms"
|
||
"fmt"
|
||
"log/slog"
|
||
"sync"
|
||
"time"
|
||
)
|
||
|
||
// TaskSuccessCallback 任务执行成功的全局回调
|
||
// 参数:taskID=任务ID, executeDuration=预期执行时长, elapsed=实际耗时
|
||
type TaskSuccessCallback func(taskID string, executeDuration time.Duration, elapsed time.Duration)
|
||
|
||
// TaskFailCallback 任务执行失败的全局回调
|
||
// 参数:taskID=任务ID, err=失败原因, elapsed=实际耗时
|
||
type TaskFailCallback func(taskID string, err error, elapsed time.Duration)
|
||
|
||
// TaskTimeoutCallback 任务执行超时的全局回调
|
||
// 参数:taskID=任务ID, timeout=超时时间, elapsed=实际耗时
|
||
type TaskTimeoutCallback func(taskID string, timeout time.Duration, elapsed time.Duration)
|
||
|
||
// TaskStartCallback 任务开始执行的全局回调
|
||
// 参数:taskID=任务ID, executeDuration=预期执行时长, timeout=超时时间
|
||
type TaskStartCallback func(task Task, executeDuration time.Duration, timeout time.Duration)
|
||
|
||
// Task 任务接口(新增动态配置方法)
|
||
type Task interface {
|
||
ID() string
|
||
Execute(ctx context.Context) error
|
||
// 读取配置(带锁)
|
||
Timeout() time.Duration
|
||
GetHandleInfo() vqdcms.VQDHandleInfo
|
||
GetPara() vqdcms.VQDPara
|
||
ExecuteDuration() time.Duration
|
||
// 动态修改配置
|
||
SetTimeout(d time.Duration)
|
||
SetExecuteDuration(d time.Duration)
|
||
}
|
||
|
||
// TaskFlow 任务流管理器(支持动态修改并发数)
|
||
type TaskFlow struct {
|
||
runningTasks map[string]Task // 正在运行的任务
|
||
waitingTasks []Task // 等待执行的任务
|
||
maxConcurrent int // 最大并发数(可动态修改)
|
||
TaskTemplate *vqd.VqdTaskTemplate // 关联模板
|
||
mu sync.RWMutex // 并发安全锁
|
||
wg sync.WaitGroup // 等待所有goroutine结束
|
||
ctx context.Context // 全局上下文
|
||
cancel context.CancelFunc // 全局取消函数
|
||
removedTasks map[string]bool // 已标记删除的任务ID
|
||
rmMutex sync.RWMutex // 删除标记的锁
|
||
closed bool // 标记是否已完全关闭
|
||
concurrentMu sync.RWMutex // 并发数修改专用锁
|
||
// ------------------- 回调相关字段 -------------------
|
||
callbackMu sync.RWMutex // 回调操作专用锁
|
||
startCallback TaskStartCallback // 开始执行回调
|
||
successCallback TaskSuccessCallback // 成功回调
|
||
failCallback TaskFailCallback // 失败回调
|
||
timeoutCallback TaskTimeoutCallback // 超时回调
|
||
}
|
||
|
||
// NewTaskFlow 创建任务流实例
|
||
func NewTaskFlow(maxConcurrent int) *TaskFlow {
|
||
ctx, cancel := context.WithCancel(context.Background())
|
||
return &TaskFlow{
|
||
runningTasks: make(map[string]Task),
|
||
waitingTasks: make([]Task, 0),
|
||
maxConcurrent: maxConcurrent,
|
||
ctx: ctx,
|
||
cancel: cancel,
|
||
removedTasks: make(map[string]bool),
|
||
closed: false,
|
||
}
|
||
}
|
||
func (tf *TaskFlow) SetTemplate(Temp *vqd.VqdTaskTemplate) {
|
||
// 加锁修改
|
||
tf.concurrentMu.Lock()
|
||
tf.TaskTemplate = Temp
|
||
tf.concurrentMu.Unlock()
|
||
}
|
||
|
||
// SetMaxConcurrent 动态修改最大并发数
|
||
// 返回值:true=修改成功,false=任务流已关闭
|
||
func (tf *TaskFlow) SetMaxConcurrent(newConcurrent int) bool {
|
||
// 检查是否已关闭
|
||
tf.mu.RLock()
|
||
closed := tf.closed
|
||
tf.mu.RUnlock()
|
||
if closed {
|
||
slog.Error("任务流已关闭,拒绝修改并发数")
|
||
return false
|
||
}
|
||
|
||
if newConcurrent <= 0 {
|
||
slog.Error("并发数必须大于0,修改失败")
|
||
return false
|
||
}
|
||
|
||
// 加锁修改
|
||
tf.concurrentMu.Lock()
|
||
oldConcurrent := tf.maxConcurrent
|
||
tf.maxConcurrent = newConcurrent
|
||
tf.concurrentMu.Unlock()
|
||
|
||
slog.Debug("最大并发数已修改为", "old", oldConcurrent, "news", newConcurrent)
|
||
return true
|
||
}
|
||
|
||
// AddTask 添加任务到等待队列(锁安全)
|
||
func (tf *TaskFlow) AddTask(task Task) {
|
||
// 检查是否已关闭
|
||
tf.mu.RLock()
|
||
closed := tf.closed
|
||
tf.mu.RUnlock()
|
||
if closed {
|
||
slog.Error("任务流已关闭,拒绝添加任务", "id", task.ID())
|
||
return
|
||
}
|
||
|
||
// 检查是否被标记删除
|
||
tf.rmMutex.RLock()
|
||
isRemoved := tf.removedTasks[task.ID()]
|
||
tf.rmMutex.RUnlock()
|
||
if isRemoved {
|
||
slog.Error("任务已被标记删除,跳过添加", "id", task.ID())
|
||
return
|
||
}
|
||
|
||
// 加写锁,确保并发安全
|
||
tf.mu.Lock()
|
||
defer tf.mu.Unlock()
|
||
|
||
// 二次检查关闭状态
|
||
if tf.closed {
|
||
slog.Error("任务流已关闭,拒绝添加任务", "id", task.ID())
|
||
return
|
||
}
|
||
|
||
// 避免重复添加
|
||
for _, t := range tf.waitingTasks {
|
||
if t.ID() == task.ID() {
|
||
return
|
||
}
|
||
}
|
||
if _, exists := tf.runningTasks[task.ID()]; exists {
|
||
return
|
||
}
|
||
|
||
tf.waitingTasks = append(tf.waitingTasks, task)
|
||
slog.Debug(fmt.Sprintf("任务 %s 已加入等待队列(执行时长: %v, 超时时间: %v)\n", task.ID(), task.ExecuteDuration(), task.Timeout()))
|
||
}
|
||
|
||
// RemoveTask 删除指定ID的任务(锁安全)
|
||
func (tf *TaskFlow) RemoveTask(taskID string, immediate bool) bool {
|
||
// 检查是否已关闭
|
||
tf.mu.RLock()
|
||
closed := tf.closed
|
||
tf.mu.RUnlock()
|
||
if closed {
|
||
slog.Error("任务流已关闭,拒绝删除任务", "id", taskID)
|
||
return false
|
||
}
|
||
|
||
// 标记任务为删除
|
||
tf.rmMutex.Lock()
|
||
tf.removedTasks[taskID] = true
|
||
tf.rmMutex.Unlock()
|
||
|
||
// 加写锁处理任务队列
|
||
tf.mu.Lock()
|
||
defer tf.mu.Unlock()
|
||
|
||
removed := false
|
||
|
||
// 1. 移除等待队列中的任务
|
||
newWaiting := make([]Task, 0, len(tf.waitingTasks))
|
||
for _, task := range tf.waitingTasks {
|
||
if task.ID() == taskID {
|
||
removed = true
|
||
slog.Debug("从等待队列中删除任务", "id", task.ID())
|
||
continue
|
||
}
|
||
newWaiting = append(newWaiting, task)
|
||
}
|
||
tf.waitingTasks = newWaiting
|
||
|
||
// 2. 处理运行中的任务
|
||
if _, exists := tf.runningTasks[taskID]; exists {
|
||
removed = true
|
||
if immediate {
|
||
delete(tf.runningTasks, taskID)
|
||
slog.Debug("标记任务为删除,立即终止当前执行", "id", taskID)
|
||
} else {
|
||
slog.Debug("标记任务为删除,执行完当前周期后移除", "id", taskID)
|
||
}
|
||
}
|
||
|
||
// 清理无效标记
|
||
if !removed {
|
||
slog.Debug("任务不存在,删除失败", "id", taskID)
|
||
tf.rmMutex.Lock()
|
||
delete(tf.removedTasks, taskID)
|
||
tf.rmMutex.Unlock()
|
||
}
|
||
|
||
return removed
|
||
}
|
||
|
||
// CleanupRemovedTasks 清理已删除任务的残留标记和资源
|
||
func (tf *TaskFlow) CleanupRemovedTasks() int {
|
||
// 检查是否已关闭
|
||
tf.mu.RLock()
|
||
closed := tf.closed
|
||
tf.mu.RUnlock()
|
||
if closed {
|
||
slog.Error("任务流已关闭,拒绝执行清理操作")
|
||
return 0
|
||
}
|
||
|
||
// 步骤1:收集有效任务ID(读锁)
|
||
tf.mu.RLock()
|
||
validTaskIDs := make(map[string]bool)
|
||
for taskID := range tf.runningTasks {
|
||
validTaskIDs[taskID] = true
|
||
}
|
||
for _, task := range tf.waitingTasks {
|
||
validTaskIDs[task.ID()] = true
|
||
}
|
||
tf.mu.RUnlock()
|
||
|
||
// 步骤2:清理无效删除标记
|
||
tf.rmMutex.Lock()
|
||
cleanedCount := 0
|
||
for taskID := range tf.removedTasks {
|
||
if !validTaskIDs[taskID] {
|
||
delete(tf.removedTasks, taskID)
|
||
cleanedCount++
|
||
slog.Debug("清理无效删除标记", "id", taskID)
|
||
}
|
||
}
|
||
tf.rmMutex.Unlock()
|
||
|
||
// 步骤3:清理等待队列残留
|
||
tf.mu.Lock()
|
||
newWaiting := make([]Task, 0, len(tf.waitingTasks))
|
||
for _, task := range tf.waitingTasks {
|
||
tf.rmMutex.RLock()
|
||
isRemoved := tf.removedTasks[task.ID()]
|
||
tf.rmMutex.RUnlock()
|
||
if !isRemoved {
|
||
newWaiting = append(newWaiting, task)
|
||
} else {
|
||
slog.Debug("清理等待队列中已删除的任务", "id", task.ID())
|
||
cleanedCount++
|
||
}
|
||
}
|
||
tf.waitingTasks = newWaiting
|
||
tf.mu.Unlock()
|
||
|
||
slog.Debug("清理完成,共清理无效标记/残留任务", "count", cleanedCount)
|
||
return cleanedCount
|
||
}
|
||
|
||
// Start 启动任务流
|
||
func (tf *TaskFlow) Start() {
|
||
tf.mu.RLock()
|
||
closed := tf.closed
|
||
tf.mu.RUnlock()
|
||
if closed {
|
||
slog.Error("任务流已关闭,无法启动")
|
||
return
|
||
}
|
||
|
||
go tf.scheduleLoop()
|
||
slog.Info("任务流已启动")
|
||
}
|
||
|
||
// Close 完全释放整个任务流
|
||
func (tf *TaskFlow) Close() {
|
||
tf.mu.Lock()
|
||
defer tf.mu.Unlock()
|
||
|
||
if tf.closed {
|
||
slog.Error("任务流已处于关闭状态,无需重复关闭")
|
||
return
|
||
}
|
||
|
||
// 标记为已关闭
|
||
tf.closed = true
|
||
slog.Info("开始完全释放任务流资源...")
|
||
|
||
// 取消全局上下文
|
||
tf.cancel()
|
||
|
||
// 等待所有goroutine退出(临时解锁避免死锁)
|
||
tf.mu.Unlock()
|
||
tf.wg.Wait()
|
||
tf.mu.Lock()
|
||
|
||
// 清空所有队列
|
||
tf.waitingTasks = nil
|
||
tf.runningTasks = nil
|
||
|
||
// 清空删除标记
|
||
tf.rmMutex.Lock()
|
||
tf.removedTasks = nil
|
||
tf.rmMutex.Unlock()
|
||
|
||
slog.Info("任务流已完全关闭,所有资源已释放")
|
||
}
|
||
|
||
// Reset 重置任务流
|
||
func (tf *TaskFlow) Reset(maxConcurrent int) {
|
||
tf.Close()
|
||
|
||
tf.mu.Lock()
|
||
defer tf.mu.Unlock()
|
||
|
||
// 重建上下文
|
||
ctx, cancel := context.WithCancel(context.Background())
|
||
tf.ctx = ctx
|
||
tf.cancel = cancel
|
||
|
||
// 重建队列
|
||
tf.waitingTasks = make([]Task, 0)
|
||
tf.runningTasks = make(map[string]Task)
|
||
tf.removedTasks = make(map[string]bool)
|
||
|
||
// 重置状态
|
||
tf.closed = false
|
||
tf.maxConcurrent = maxConcurrent
|
||
|
||
slog.Debug("任务流已重置,最大并发数", "count", maxConcurrent)
|
||
}
|
||
|
||
// scheduleLoop 任务调度主循环
|
||
func (tf *TaskFlow) scheduleLoop() {
|
||
ticker := time.NewTicker(100 * time.Millisecond)
|
||
defer ticker.Stop()
|
||
|
||
for {
|
||
select {
|
||
case <-tf.ctx.Done():
|
||
slog.Info("任务调度循环已停止")
|
||
return
|
||
case <-ticker.C:
|
||
// 检查是否已关闭
|
||
tf.mu.RLock()
|
||
closed := tf.closed
|
||
tf.mu.RUnlock()
|
||
if closed {
|
||
return
|
||
}
|
||
tf.scheduleTasks()
|
||
}
|
||
}
|
||
}
|
||
|
||
// scheduleTasks 调度任务执行(动态读取并发数)
|
||
func (tf *TaskFlow) scheduleTasks() {
|
||
// 读取当前运行数和最新并发数
|
||
tf.mu.RLock()
|
||
currentRunning := len(tf.runningTasks)
|
||
tf.mu.RUnlock()
|
||
|
||
tf.concurrentMu.RLock()
|
||
maxConcurrent := tf.maxConcurrent
|
||
tf.concurrentMu.RUnlock()
|
||
|
||
availableSlots := maxConcurrent - currentRunning
|
||
|
||
// 检查是否已关闭
|
||
tf.mu.RLock()
|
||
closed := tf.closed
|
||
tf.mu.RUnlock()
|
||
|
||
if closed || availableSlots <= 0 {
|
||
return
|
||
}
|
||
|
||
// 取出待执行任务
|
||
tf.mu.Lock()
|
||
tasksToRun := make([]Task, 0)
|
||
if len(tf.waitingTasks) > 0 {
|
||
take := availableSlots
|
||
if take > len(tf.waitingTasks) {
|
||
take = len(tf.waitingTasks)
|
||
}
|
||
tasksToRun = tf.waitingTasks[:take]
|
||
tf.waitingTasks = tf.waitingTasks[take:]
|
||
}
|
||
tf.mu.Unlock()
|
||
|
||
// 执行任务
|
||
for _, task := range tasksToRun {
|
||
tf.runTask(task)
|
||
}
|
||
}
|
||
|
||
// runTask 执行单个任务(使用最新的任务配置)
|
||
func (tf *TaskFlow) runTask(task Task) {
|
||
taskID := task.ID()
|
||
|
||
// 加写锁标记为运行中
|
||
tf.mu.Lock()
|
||
// 检查状态
|
||
if tf.closed {
|
||
tf.mu.Unlock()
|
||
slog.Error("任务流已关闭,取消执行任务", "id", taskID)
|
||
return
|
||
}
|
||
tf.rmMutex.RLock()
|
||
isRemoved := tf.removedTasks[taskID]
|
||
tf.rmMutex.RUnlock()
|
||
if isRemoved {
|
||
tf.mu.Unlock()
|
||
slog.Debug("任务已被删除,取消执行", "id", taskID)
|
||
return
|
||
}
|
||
// 标记为运行中
|
||
tf.runningTasks[taskID] = task
|
||
tf.mu.Unlock()
|
||
|
||
// 执行任务协程
|
||
tf.wg.Add(1)
|
||
go func(t Task) {
|
||
defer tf.wg.Done()
|
||
|
||
// 获取任务最新的超时配置(执行时读取,保证使用最新值)
|
||
timeout := t.Timeout()
|
||
executeDuration := t.ExecuteDuration()
|
||
|
||
// 创建超时上下文(使用最新超时时间)
|
||
taskCtx, taskCancel := context.WithTimeout(tf.ctx, timeout)
|
||
defer taskCancel()
|
||
tf.callbackMu.RLock()
|
||
defer tf.callbackMu.RUnlock()
|
||
if tf.startCallback != nil {
|
||
tf.startCallback(t, executeDuration, timeout)
|
||
}
|
||
slog.Debug(fmt.Sprintf("开始执行任务: %s (执行时长: %v, 超时时间: %v)", t.ID(), executeDuration, timeout))
|
||
startTime := time.Now()
|
||
|
||
// 执行任务
|
||
err := t.Execute(taskCtx)
|
||
elapsed := time.Since(startTime)
|
||
|
||
// 清理运行标记
|
||
tf.mu.Lock()
|
||
delete(tf.runningTasks, t.ID())
|
||
tf.mu.Unlock()
|
||
|
||
// 检查状态
|
||
tf.rmMutex.RLock()
|
||
isRemoved = tf.removedTasks[t.ID()]
|
||
tf.rmMutex.RUnlock()
|
||
tf.mu.RLock()
|
||
closed := tf.closed
|
||
tf.mu.RUnlock()
|
||
|
||
// 处理执行结果
|
||
if err != nil {
|
||
tf.callbackMu.RLock()
|
||
defer tf.callbackMu.RUnlock()
|
||
// 区分超时和普通失败
|
||
if ctxErr := taskCtx.Err(); ctxErr == context.DeadlineExceeded {
|
||
// 超时触发超时回调
|
||
if tf.timeoutCallback != nil {
|
||
tf.timeoutCallback(t.ID(), timeout, elapsed)
|
||
}
|
||
} else {
|
||
// 普通失败触发失败回调
|
||
if tf.failCallback != nil {
|
||
tf.failCallback(t.ID(), err, elapsed)
|
||
}
|
||
}
|
||
slog.Debug(fmt.Sprintf("任务 %s 执行失败: %v (耗时: %v)", t.ID(), err, elapsed))
|
||
if !closed && !isRemoved {
|
||
tf.AddTask(t)
|
||
} else {
|
||
slog.Debug("任务已被删除/任务流关闭,失败后不重试", "id", t.ID())
|
||
}
|
||
} else {
|
||
tf.callbackMu.RLock()
|
||
defer tf.callbackMu.RUnlock()
|
||
if tf.successCallback != nil {
|
||
tf.successCallback(t.ID(), executeDuration, elapsed)
|
||
}
|
||
slog.Debug(fmt.Sprintf("任务 %s 执行成功 (预期执行时长: %v, 实际耗时: %v)", t.ID(), executeDuration, elapsed))
|
||
if !closed && !isRemoved {
|
||
tf.AddTask(t)
|
||
} else {
|
||
slog.Debug("任务已被删除/任务流关闭,成功后不再循环", "id", t.ID())
|
||
tf.rmMutex.Lock()
|
||
delete(tf.removedTasks, t.ID())
|
||
tf.rmMutex.Unlock()
|
||
}
|
||
}
|
||
}(task)
|
||
}
|
||
|
||
// BatchSetTimeout 批量给【所有任务】设置 超时时间
|
||
func (tf *TaskFlow) BatchSetTimeout(timeout time.Duration) {
|
||
tf.mu.RLock()
|
||
defer tf.mu.RUnlock()
|
||
|
||
if tf.closed {
|
||
slog.Error("任务流已关闭,批量设置超时失败")
|
||
return
|
||
}
|
||
|
||
// 设置等待队列所有任务
|
||
for _, task := range tf.waitingTasks {
|
||
task.SetTimeout(timeout)
|
||
}
|
||
// 设置运行中任务
|
||
for _, task := range tf.runningTasks {
|
||
task.SetTimeout(timeout)
|
||
}
|
||
slog.Debug("所有任务超时时间已批量设置为", "time", timeout)
|
||
}
|
||
|
||
// BatchSetExecuteDuration 批量给【所有任务】设置 执行时长
|
||
func (tf *TaskFlow) BatchSetExecuteDuration(duration time.Duration) {
|
||
tf.mu.RLock()
|
||
defer tf.mu.RUnlock()
|
||
|
||
if tf.closed {
|
||
slog.Error("任务流已关闭,批量设置执行时长失败")
|
||
return
|
||
}
|
||
|
||
// 设置等待队列
|
||
for _, task := range tf.waitingTasks {
|
||
task.SetExecuteDuration(duration)
|
||
}
|
||
// 设置运行中任务
|
||
for _, task := range tf.runningTasks {
|
||
task.SetExecuteDuration(duration)
|
||
}
|
||
slog.Debug("所有任务执行时长已批量设置为", "duration", duration)
|
||
}
|
||
|
||
// BatchSetByIDs 按任务ID列表批量设置 超时+执行时长
|
||
func (tf *TaskFlow) BatchSetByIDs(ids []string, timeout, duration time.Duration) {
|
||
tf.mu.RLock()
|
||
defer tf.mu.RUnlock()
|
||
|
||
if tf.closed {
|
||
slog.Error("任务流已关闭,按ID批量设置失败")
|
||
return
|
||
}
|
||
|
||
idSet := make(map[string]bool)
|
||
for _, id := range ids {
|
||
idSet[id] = true
|
||
}
|
||
|
||
// 处理等待队列
|
||
for _, task := range tf.waitingTasks {
|
||
if idSet[task.ID()] {
|
||
task.SetTimeout(timeout)
|
||
task.SetExecuteDuration(duration)
|
||
}
|
||
}
|
||
// 处理运行中队列
|
||
for _, task := range tf.runningTasks {
|
||
if idSet[task.ID()] {
|
||
task.SetTimeout(timeout)
|
||
task.SetExecuteDuration(duration)
|
||
}
|
||
}
|
||
tf.UnregisterAllCallbacks()
|
||
slog.Debug("所有任务执行按ID批量设置完成", "超时", timeout, "执行时长", duration)
|
||
}
|
||
|
||
// 判断是否在执行
|
||
func (tf *TaskFlow) HasRunning(taskId string) bool {
|
||
tf.mu.RLock()
|
||
defer tf.mu.RUnlock()
|
||
if tf.closed {
|
||
slog.Error("任务流已关闭")
|
||
return false
|
||
}
|
||
flag := false
|
||
// 处理运行中队列
|
||
for _, task := range tf.runningTasks {
|
||
if task.ID() == taskId {
|
||
flag = true
|
||
break
|
||
}
|
||
}
|
||
return flag
|
||
}
|
||
|
||
// RegisterSuccessCallback 注册任务成功的全局回调
|
||
func (tf *TaskFlow) RegisterSuccessCallback(callback TaskSuccessCallback) {
|
||
tf.callbackMu.Lock()
|
||
defer tf.callbackMu.Unlock()
|
||
tf.successCallback = callback
|
||
slog.Debug("任务成功全局回调已注册")
|
||
}
|
||
|
||
// RegisterFailCallback 注册任务失败的全局回调
|
||
func (tf *TaskFlow) RegisterFailCallback(callback TaskFailCallback) {
|
||
tf.callbackMu.Lock()
|
||
defer tf.callbackMu.Unlock()
|
||
tf.failCallback = callback
|
||
slog.Debug("任务失败全局回调已注册")
|
||
}
|
||
|
||
// RegisterTimeoutCallback 注册任务超时的全局回调
|
||
func (tf *TaskFlow) RegisterTimeoutCallback(callback TaskTimeoutCallback) {
|
||
tf.callbackMu.Lock()
|
||
defer tf.callbackMu.Unlock()
|
||
tf.timeoutCallback = callback
|
||
slog.Debug("任务超时全局回调已注册")
|
||
}
|
||
|
||
// RegisterStartCallback 注册任务开始执行的全局回调
|
||
func (tf *TaskFlow) RegisterStartCallback(callback TaskStartCallback) {
|
||
tf.callbackMu.Lock()
|
||
defer tf.callbackMu.Unlock()
|
||
tf.startCallback = callback
|
||
slog.Debug("任务开始执行全局回调已注册")
|
||
}
|
||
|
||
// UnregisterAllCallbacks 注销所有全局回调
|
||
func (tf *TaskFlow) UnregisterAllCallbacks() {
|
||
tf.callbackMu.Lock()
|
||
defer tf.callbackMu.Unlock()
|
||
tf.successCallback = nil
|
||
tf.failCallback = nil
|
||
tf.timeoutCallback = nil
|
||
tf.startCallback = nil
|
||
slog.Info("所有全局回调已注销")
|
||
}
|