github.com/zly-app/zapp@v1.3.3/component/gpool/gpool.go (about)

     1  /*
     2  -------------------------------------------------
     3     Author :       zlyuancn
     4     date:         2021/3/19
     5     Description :
     6  -------------------------------------------------
     7  */
     8  
     9  package gpool
    10  
    11  import (
    12  	"sync"
    13  
    14  	"github.com/zly-app/zapp/core"
    15  )
    16  
    17  // 协程池
    18  type gpool struct {
    19  	workerQueue chan *worker  // 工人队列
    20  	jobQueue    chan *job     // 任务队列
    21  	stop        chan struct{} // 停止信号, 同步通道
    22  
    23  	wg sync.WaitGroup
    24  }
    25  
    26  func NewGPool(conf *GPoolConfig) core.IGPool {
    27  	conf.check()
    28  	if conf.ThreadCount < 0 {
    29  		return NewNoPool()
    30  	}
    31  
    32  	g := &gpool{
    33  		workerQueue: make(chan *worker, conf.ThreadCount),
    34  		jobQueue:    make(chan *job, conf.JobQueueSize),
    35  		stop:        make(chan struct{}),
    36  	}
    37  
    38  	for i := 0; i < conf.ThreadCount; i++ {
    39  		worker := newWorker(g.workerQueue)
    40  		worker.Ready()
    41  		g.workerQueue <- worker
    42  	}
    43  
    44  	go g.dispatch()
    45  
    46  	return g
    47  }
    48  
    49  // 为工人派遣任务
    50  func (g *gpool) dispatch() {
    51  	var worker *worker
    52  	var stop bool
    53  	for !stop {
    54  		if worker == nil {
    55  			select {
    56  			case w := <-g.workerQueue:
    57  				worker = w
    58  			case <-g.stop:
    59  				stop = true
    60  				continue
    61  			}
    62  		}
    63  
    64  		select {
    65  		case job := <-g.jobQueue:
    66  			worker.Do(job)
    67  			worker = nil
    68  		case <-g.stop:
    69  			stop = true
    70  		}
    71  	}
    72  
    73  	// 释放worker
    74  	if worker != nil {
    75  		worker.Do(nil) // 让这个工人回到池
    76  	}
    77  	for i := 0; i < cap(g.workerQueue); i++ {
    78  		w := <-g.workerQueue
    79  		w.Stop()
    80  	}
    81  	g.workerQueue = nil
    82  
    83  	// 释放job
    84  	jobLen := len(g.jobQueue)
    85  	g.jobQueue = nil
    86  	for i := 0; i < jobLen; i++ {
    87  		g.wg.Done()
    88  	}
    89  
    90  	g.stop <- struct{}{}
    91  }
    92  
    93  // 异步执行, 如果队列任务已满则阻塞等待直到有空位
    94  func (g *gpool) Go(fn func() error, callback func(err error)) {
    95  	job := g.newJob(fn, callback)
    96  	g.jobQueue <- job
    97  }
    98  
    99  // 同步执行
   100  func (g *gpool) GoSync(fn func() error) (result error) {
   101  	var wg sync.WaitGroup
   102  	wg.Add(1)
   103  	g.Go(fn, func(err error) {
   104  		result = err
   105  		wg.Done()
   106  	})
   107  	wg.Wait()
   108  	return result
   109  }
   110  
   111  // 尝试异步执行, 如果任务队列已满则返回false
   112  func (g *gpool) TryGo(fn func() error, callback func(err error)) (ok bool) {
   113  	job := g.newJob(fn, callback)
   114  	select {
   115  	case g.jobQueue <- job:
   116  		return true
   117  	default:
   118  		g.wg.Done()
   119  		return false
   120  	}
   121  }
   122  
   123  // 尝试同步执行, 如果任务队列已满则返回false
   124  func (g *gpool) TryGoSync(fn func() error) (result error, ok bool) {
   125  	var wg sync.WaitGroup
   126  	wg.Add(1)
   127  	ok = g.TryGo(fn, func(err error) {
   128  		result = err
   129  		wg.Done()
   130  	})
   131  	if ok {
   132  		wg.Wait()
   133  	}
   134  	return
   135  }
   136  
   137  // 执行等待所有函数完成
   138  func (g *gpool) GoAndWait(fn ...func() error) error {
   139  	if len(fn) == 0 {
   140  		return nil
   141  	}
   142  
   143  	var wg sync.WaitGroup
   144  	errChan := make(chan error, len(fn))
   145  
   146  	for _, f := range fn {
   147  		wg.Add(1)
   148  		g.Go(f, func(err error) {
   149  			if err != nil {
   150  				errChan <- err
   151  			}
   152  			wg.Done()
   153  		})
   154  	}
   155  	wg.Wait()
   156  
   157  	var err error
   158  	select {
   159  	case err = <-errChan:
   160  	default:
   161  	}
   162  	return err
   163  }
   164  
   165  // 等待队列中所有的任务结束
   166  func (g *gpool) Wait() {
   167  	g.wg.Wait()
   168  }
   169  
   170  // 关闭
   171  //
   172  // 命令所有没有收到任务的工人立即停工, 收到任务的工人完成当前任务后停工, 不管任务队列是否清空.
   173  // 表现为加入队列的任务不一定会执行, 但是正在执行的任务不会被取消并会等待这些任务执行完毕.
   174  func (g *gpool) Close() {
   175  	g.stop <- struct{}{}
   176  	<-g.stop
   177  }
   178  
   179  func (g *gpool) newJob(fn func() error, callback func(err error)) *job {
   180  	g.wg.Add(1)
   181  	return newJob(fn, func(err error) {
   182  		g.wg.Done()
   183  		if callback != nil {
   184  			callback(err)
   185  		}
   186  	})
   187  }