github.com/zhiqiangxu/util@v0.0.0-20230112053021-0a7aee056cd5/parallel/parallel.go (about) 1 package parallel 2 3 import ( 4 "context" 5 "fmt" 6 "sync" 7 "sync/atomic" 8 "time" 9 10 zlog "github.com/rs/zerolog/log" 11 ) 12 13 type Result struct { 14 R interface{} 15 I int 16 } 17 18 func First[R any](ctx context.Context, workers int, handleFunc func(ctx context.Context, workerIdx int) (R, error), cd time.Duration) (r R, i int, err error) { 19 if workers == 0 { 20 err = fmt.Errorf("workers == 0") 21 return 22 } 23 24 if workers == 1 { 25 for { 26 r, err = handleFunc(ctx, 0) 27 if err == nil { 28 return 29 } 30 31 select { 32 case <-ctx.Done(): 33 return 34 default: 35 } 36 time.Sleep(cd) 37 } 38 } 39 40 resultCh := make(chan Result, 1) 41 doneCh := make(chan struct{}) 42 for i := 0; i < workers; i++ { 43 go func(i int) { 44 var ( 45 r R 46 err error 47 ) 48 for { 49 r, err = handleFunc(ctx, i) 50 if err != nil { 51 select { 52 case <-doneCh: 53 return 54 case <-ctx.Done(): 55 return 56 default: 57 } 58 time.Sleep(cd) 59 continue 60 } 61 62 select { 63 case resultCh <- Result{R: r, I: i}: 64 default: 65 } 66 return 67 } 68 69 }(i) 70 } 71 72 select { 73 case result := <-resultCh: 74 r = result.R.(R) 75 i = result.I 76 close(doneCh) 77 case <-ctx.Done(): 78 err = ctx.Err() 79 } 80 81 return 82 } 83 84 type subTask struct { 85 from, to int 86 } 87 88 func All(ctx context.Context, total, unit, workers int, handleFunc func(ctx context.Context, workerIdx int, from int, to int) error, dispatchCB func(int, int), retry int, cd time.Duration) { 89 if workers == 0 { 90 panic("workers == 0") 91 } 92 93 taskChan := make(chan subTask, workers) 94 doneCh := make(chan struct{}) 95 96 var ( 97 wg sync.WaitGroup 98 doneCount int64 99 ) 100 wg.Add(workers) 101 for i := 0; i < workers; i++ { 102 go func(i int) { 103 defer wg.Done() 104 105 var ( 106 err error 107 task subTask 108 ) 109 for { 110 select { 111 case task = <-taskChan: 112 for k := 0; k < retry; k++ { 113 err = handleFunc(ctx, i, task.from, task.to) 114 if err != nil { 115 if k == retry-1 { 116 break 117 } 118 zlog.Warn().Int("i", i).Err(err).Msg("handleFunc") 119 time.Sleep(cd) 120 continue 121 } 122 if atomic.AddInt64(&doneCount, int64(task.to-task.from)) == int64(total) { 123 close(doneCh) 124 return 125 } 126 break 127 } 128 if err != nil { 129 // switch a worker and try again 130 select { 131 case taskChan <- task: 132 case <-ctx.Done(): 133 return 134 } 135 time.Sleep(cd) 136 } 137 138 case <-ctx.Done(): 139 return 140 case <-doneCh: 141 return 142 } 143 } 144 }(i) 145 } 146 147 for from := 0; from < total; from += unit { 148 to := from + unit 149 if to > total { 150 to = total 151 } 152 if dispatchCB != nil { 153 dispatchCB(from, to) 154 } 155 select { 156 case taskChan <- subTask{from: from, to: to}: 157 case <-ctx.Done(): 158 return 159 } 160 } 161 162 select { 163 case <-doneCh: 164 case <-ctx.Done(): 165 } 166 167 wg.Wait() 168 return 169 }