github.com/weedge/lib@v0.0.0-20230424045628-a36dcc1d90e4/pool/workerpool/worker.go (about)

     1  package workerpool
     2  
     3  import (
     4  	"github.com/weedge/lib/runtimer"
     5  	"github.com/weedge/lib/strings"
     6  	"runtime"
     7  	"runtime/debug"
     8  	"sync/atomic"
     9  	"time"
    10  
    11  	"github.com/weedge/lib/log"
    12  )
    13  
    14  type Worker struct {
    15  	timer                 *time.Timer
    16  	myWorkerPool          *WorkerPool //所属任务工作池
    17  	hasGoroutineRunning   int32       //是否有对应的goroutine在运行
    18  	chTaskIsTimeOut       chan<- bool //任务是否超时,true 超时,false 正常 由添加任务时创建
    19  	chExecuteGoroutineOut chan int    //执行任务的goroutine结束 -> worker goroutine 结束
    20  }
    21  
    22  func newWorker(wp *WorkerPool) *Worker {
    23  	return &Worker{
    24  		timer:                 time.NewTimer(DefaultTimeOut),
    25  		myWorkerPool:          wp,
    26  		chExecuteGoroutineOut: make(chan int, 1),
    27  	}
    28  }
    29  
    30  func (worker *Worker) safelyDo() {
    31  	if nil == worker.myWorkerPool {
    32  		log.Error("myWorkerPool ptr is nil ")
    33  		return
    34  	}
    35  
    36  	debug.SetPanicOnFault(true)
    37  	defer worker.myWorkerPool.wg.Done()
    38  	defer func() {
    39  		if r := recover(); r != nil {
    40  			if worker.myWorkerPool.isWorking() {
    41  				worker.myWorkerPool.chAddWorker <- 1
    42  			}
    43  
    44  			atomic.AddInt32(&(worker.myWorkerPool.curWorkerNum), -1)
    45  			atomic.SwapInt32(&(worker.hasGoroutineRunning), 0)
    46  			log.Error("check timeout worker goroutine is crash, panic", r, strings.BytesToString(debug.Stack()))
    47  		}
    48  	}()
    49  
    50  	worker.execute()
    51  }
    52  
    53  func (worker *Worker) execute() {
    54  	for {
    55  		select {
    56  		case task, ok := <-worker.myWorkerPool.chWorkTask:
    57  			if ok {
    58  				if task.TimeOut > 0 {
    59  					worker.timer.Reset(task.TimeOut)
    60  				}
    61  				taskDoResCh := make(chan bool, 1)
    62  
    63  				runtimer.GoSafely(nil, false, func() {
    64  					taskDoResCh <- task.Do(task.InParam, task.OutParam)
    65  				}, nil, nil)
    66  
    67  				select {
    68  				case <-worker.timer.C:
    69  					if task.ChIsTimeOut != nil {
    70  						task.ChIsTimeOut <- true
    71  					}
    72  					if task.OnTimeOut != nil {
    73  						task.OnTimeOut(task.InParam, task.OutParam)
    74  					}
    75  				case _, ok := <-taskDoResCh:
    76  					if ok && task.ChIsTimeOut != nil {
    77  						task.ChIsTimeOut <- false
    78  					}
    79  				}
    80  
    81  				worker.destroyAfterTaskDone()
    82  			} else {
    83  				worker.myWorkerPool.close()
    84  				log.Info("close chWorkTask, myWorkerPool close and exit worker goroutine, the current goroutine number:", atomic.LoadInt32(&worker.myWorkerPool.curWorkerNum))
    85  				runtime.Goexit()
    86  			}
    87  		case _, ok := <-worker.chExecuteGoroutineOut:
    88  			if worker.myWorkerPool.isWorking() && atomic.LoadInt32(&worker.hasGoroutineRunning) <= 0 && ok {
    89  				log.Info("send to add worker when ExecuteGoroutineOut")
    90  				worker.myWorkerPool.chAddWorker <- 1
    91  			}
    92  			atomic.AddInt32(&(worker.myWorkerPool.curWorkerNum), -1)
    93  			log.Info(" exit this execute worker goroutine, the current goroutine number:", atomic.LoadInt32(&worker.myWorkerPool.curWorkerNum))
    94  			runtime.Goexit()
    95  		}
    96  	}
    97  }
    98  
    99  // worker destroy after task done cond:
   100  // 1. worker pool is working
   101  // 2. work task ch is empty
   102  // 3. cur worker num of pool > min worker num
   103  // 4. (now - add worker last time) > worker life time
   104  func (worker *Worker) destroyAfterTaskDone() {
   105  	chWorkTaskLen := len(worker.myWorkerPool.chWorkTask)
   106  	if worker.myWorkerPool.isWorking() && chWorkTaskLen <= 0 &&
   107  		atomic.LoadInt32(&worker.myWorkerPool.curWorkerNum) > worker.myWorkerPool.minWorkerNum &&
   108  		time.Now().Unix()-worker.myWorkerPool.addWorkerLastTime >= workerGoroutineLifeTime {
   109  		worker.chExecuteGoroutineOut <- 1
   110  		log.Info("exit more worker goroutine, the current goroutine number:", atomic.LoadInt32(&worker.myWorkerPool.curWorkerNum))
   111  		runtime.Goexit()
   112  	}
   113  }