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  }