场景背景
在很多实际应用中,我们会遇到需要同时追踪多个子任务进度的情况,常见场景包括:
- 分布式任务处理:一个大的任务被分解为多个子任务,每个子任务执行不同的工作单元。父任务的进度应当基于所有子任务的进度。
- 多阶段工作流:任务被拆分成多个阶段,父任务的进度依赖于各个阶段的执行情况。
- 并行处理:多个子任务并行执行,最终汇总父任务的进度。
为了同步这些任务的进度,我们通常需要通过某种方式将子任务的进度反馈给父任务。回调机制便是解决这一问题的有效方式。
回调机制:父子任务进度同步
核心思路
我们通过回调函数将父任务的进度更新传递给子任务。每当子任务执行进度更新时,它会通过回调通知父任务更新其进度。
步骤概述
- 父任务进度回调:父任务会在初始化时传入回调函数,每次进度更新时会调用该回调。
- 子任务进度回调:子任务在更新自己进度时,调用父任务的回调函数,从而更新父任务的进度。
- 进度同步:通过这种回调机制,父任务的进度能够随着子任务的完成而动态变化。
代码实现
我们定义了一个 StageStepper 结构来表示任务的进度管理器。该结构提供了进度步进、进度更新、以及完成任务的功能。
StageStepper 结构体
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
| package main
import "fmt"
type StageProgress interface { Step() StepBy(n int) Finish() Percent() int }
type StageStepper struct { total int current int defaultStep int onUpdate func(delta, percent int) }
func NewStageStepper(total int, defaultStep int, onUpdate func(delta, percent int)) StageProgress { if total <= 0 { total = 1 } if defaultStep <= 0 { defaultStep = 1 } if onUpdate == nil { onUpdate = func(delta, percent int) {} }
return &StageStepper{ total: total, current: 0, defaultStep: defaultStep, onUpdate: onUpdate, } }
func (s *StageStepper) Step() { s.StepBy(s.defaultStep) }
func (s *StageStepper) StepBy(n int) { if n <= 0 || s.current >= s.total { return }
remain := s.total - s.current if n > remain { n = remain }
s.current += n s.emit(n) }
func (s *StageStepper) Finish() { remain := s.total - s.current if remain > 0 { s.current = s.total s.emit(remain) } }
func (s *StageStepper) emit(delta int) { if delta <= 0 { return } s.onUpdate(delta, s.Percent()) }
func (s *StageStepper) Percent() int { if s.total <= 0 { return 100 } if s.current <= 0 { return 0 } if s.current >= s.total { return 100 } return (s.current*100 + s.total/2) / s.total }
|
使用示例
在父任务和子任务之间同步进度时,我们需要为每个任务设置回调。子任务在执行时,会通过回调更新父任务的进度。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| func main() { var wg sync.WaitGroup
parentTask := NewStageStepper(150, 10, func(delta, percent int) { fmt.Printf("Parent Task Progress: %d%% (Step: %d)\n", percent, delta) })
subTask := NewStageStepper(50, 5, func(delta, percent int) { fmt.Printf("SubTask Progress: %d%% (Step: %d)\n", percent, delta) parentTask.StepBy(delta) })
wg.Add(1) go func() { defer wg.Done() for i := 0; i < 10; i++ { parentTask.Step() } }()
wg.Add(1) go func() { defer wg.Done() for i := 0; i < 5; i++ { subTask.Step() } }()
wg.Wait()
parentTask.Finish() subTask.Finish() }
|
进度同步细节
1.父任务总步数设置为 150 步:
父任务的总步数设置为 150,其中包括了父任务的 100 步和子任务的 50 步。父任务的进度条总进度量综合了父任务和所有子任务的步数。
2. 进度计算:
父任务的进度计算同时考虑了父任务自身的进度和所有子任务的进度。在子任务完成一定步数时,父任务的进度会按比例更新。具体地,子任务的进度直接影响父任务的进度,确保父任务进度条准确反映出父任务和子任务的整体执行情况。
3. 回调机制:
通过回调机制,子任务在每次步进时,会通过回调更新父任务的进度。在子任务的进度更新回调函数中,每次子任务步进时,都会通知父任务通过 parentTask.StepBy(delta) 更新父任务的进度。这样,父任务进度条的显示会实时同步到子任务的执行情况。
输出示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| SubTask Progress: 10% (Step: 5) Parent Task Progress: 10% (Step: 5) SubTask Progress: 20% (Step: 5) Parent Task Progress: 13% (Step: 5) SubTask Progress: 30% (Step: 5) Parent Task Progress: 17% (Step: 5) SubTask Progress: 40% (Step: 5) Parent Task Progress: 20% (Step: 5) SubTask Progress: 50% (Step: 5) Parent Task Progress: 23% (Step: 5) SubTask Progress: 60% (Step: 5) Parent Task Progress: 27% (Step: 5) SubTask Progress: 70% (Step: 5) Parent Task Progress: 30% (Step: 5) SubTask Progress: 80% (Step: 5) Parent Task Progress: 33% (Step: 5) SubTask Progress: 90% (Step: 5) Parent Task Progress: 37% (Step: 5) SubTask Progress: 100% (Step: 5) Parent Task Progress: 40% (Step: 5) Parent Task Progress: 7% (Step: 10) Parent Task Progress: 47% (Step: 10) Parent Task Progress: 53% (Step: 10) Parent Task Progress: 60% (Step: 10) Parent Task Progress: 67% (Step: 10) Parent Task Progress: 73% (Step: 10) Parent Task Progress: 80% (Step: 10) Parent Task Progress: 87% (Step: 10) Parent Task Progress: 93% (Step: 10) Parent Task Progress: 100% (Step: 10) ...
|
适用场景
任务总量步进可知的情况
适用于父任务和子任务的进度总量已知的场景,通过综合计算父任务和子任务的总进度,确保父任务进度条平滑更新,避免进度条跳动不流畅。
父子任务进度汇总
在多子任务场景下,父任务进度需要动态调整,确保子任务完成时父任务进度准确反映所有子任务的执行情况。
总结
通过回调机制,我们可以在不改变现有设计结构的前提下,灵活地实现父子任务的进度同步。每当子任务步进时,它会通过回调函数通知父任务更新进度,确保父任务的进度始终反映子任务的完成情况。
这种方法在处理多级任务、并行任务及分布式系统中的任务进度管理时尤为有效。希望这篇博客能够帮助你理解如何利用 Go 中的回调机制来实现任务进度同步,从而优化你的任务调度和进度管理。