package worker import ( "context" "errors" "fmt" "sync" "time" ) type ( // Task should watch for context Task interface { Start(context.Context) error } Work struct { Name string Task Task wait time.Duration } TaskPool struct { tasks []*Work } ) func NewTaskPool() *TaskPool { return &TaskPool{} } func (w *Work) run(ctx context.Context) error { // first time fire from the get go timer := time.NewTimer(time.Nanosecond) for { select { case <-ctx.Done(): return ctx.Err() case <-timer.C: fmt.Println("Process starting: ", w.Name) if err := w.Task.Start(ctx); err != nil && !errors.Is(err, context.Canceled) { fmt.Println("Process errored: ", w.Name, err.Error()) return err } else { fmt.Println("Process done: ", w.Name) } } timer.Reset(w.wait) } } func (self *TaskPool) AddTask(name string, wait time.Duration, task Task) { self.tasks = append(self.tasks, &Work{ Name: name, Task: task, wait: wait, }) } func (self *TaskPool) Start(ctx context.Context) { var wg sync.WaitGroup wg.Add(len(self.tasks)) for _, w := range self.tasks { go func(w *Work) { _ = w.run(ctx) wg.Done() }(w) } wg.Wait() }