github.com/suiyunonghen/dxcommonlib@v0.0.0-20190612012041-7f8547a81a67/GWorkers.go (about)

     1  /*
     2  从fasthttp中变更过来的GoRoutine池
     3  Autor: 不得闲
     4  QQ:75492895
     5  */
     6  package DxCommonLib
     7  
     8  import (
     9  	"sync"
    10  	"runtime"
    11  	"time"
    12  )
    13  
    14  type (
    15  	//任务
    16  	ITaskRunner interface {
    17  		Run()
    18  	}
    19  
    20  	GWorkerFunc func(data ...interface{})
    21  	GWorkers    struct {
    22  		fMaxWorkersCount		int				//能同时存在的最多线程个数
    23  		fMaxWorkerIdleTime		time.Duration	//线程能空闲的最长时间,超过这个时间了会回收掉这个线程
    24  		ready 					[]*workerChan   //准备好的空闲线程
    25  		lock           	   		sync.Mutex
    26  		workerChanPool 			sync.Pool		//工作者
    27  		deftaskrunnerPool		sync.Pool		//默认任务
    28  		mustStop				bool
    29  		fstopchan  				chan struct{}
    30  		workersCount 			int
    31  	}
    32  
    33  	workerChan struct {
    34  		lastUseTime		time.Time
    35  		fOwner			*GWorkers
    36  		fcurTask		chan ITaskRunner
    37  	}
    38  
    39  	defTaskRunner struct {
    40  		runfunc GWorkerFunc
    41  		runargs []interface{}
    42  	}
    43  )
    44  
    45  func (runner *defTaskRunner) Run() {
    46  	runner.runfunc(runner.runargs...)
    47  }
    48  
    49  func (workers *GWorkers) Start() {
    50  	if workers.fstopchan != nil {
    51  		panic("BUG: GWorkers already started")
    52  	}
    53  	workers.fstopchan = make(chan struct{})
    54  	stopCh := workers.fstopchan
    55  	go func() {
    56  		var scratch []*workerChan
    57  		for {
    58  			select {
    59  			case <-stopCh:
    60  				return
    61  			case <-After(workers.fMaxWorkerIdleTime):
    62  				workers.clean(&scratch)//定时执行清理回收线程
    63  			}
    64  		}
    65  	}()
    66  }
    67  
    68  
    69  func (workers *GWorkers) Stop() {
    70  	if workers.fstopchan == nil {
    71  		panic("BUG: GWorkers wasn't started")
    72  	}
    73  	close(workers.fstopchan)
    74  	workers.fstopchan = nil
    75  
    76  	// Stop all the workers waiting for incoming connections.
    77  	// Do not wait for busy workers - they will stop after
    78  	// serving the connection and noticing wp.mustStop = true.
    79  	workers.lock.Lock()
    80  	ready := workers.ready
    81  	for i, ch := range ready {
    82  		ch.fcurTask <- nil
    83  		ready[i] = nil
    84  	}
    85  	workers.ready = ready[:0]
    86  	workers.mustStop = true
    87  	workers.lock.Unlock()
    88  }
    89  
    90  
    91  var workerChanCap = func() int {
    92  	// Use blocking workerChan if GOMAXPROCS=1.
    93  	// This immediately switches Serve to WorkerFunc, which results
    94  	// in higher performance (under go1.5 at least).
    95  	if runtime.GOMAXPROCS(0) == 1 {
    96  		return 0
    97  	}
    98  
    99  	// Use non-blocking workerChan if GOMAXPROCS>1,
   100  	// since otherwise the Serve caller (Acceptor) may lag accepting
   101  	// new connections if WorkerFunc is CPU-bound.
   102  	return 1
   103  }()
   104  
   105  func (workers *GWorkers) clean(scratch *[]*workerChan) {
   106  	maxIdleWorkerDuration := workers.fMaxWorkerIdleTime
   107  
   108  	// Clean least recently used workers if they didn't serve connections
   109  	// for more than maxIdleWorkerDuration.
   110  	currentTime := time.Now()
   111  	workers.lock.Lock()
   112  	ready := workers.ready
   113  	n := len(ready)
   114  	i := 0
   115  	//超过设定的最大空闲时间的,就解雇掉
   116  	for i < n && currentTime.Sub(ready[i].lastUseTime) > maxIdleWorkerDuration {
   117  		i++
   118  	}
   119  	*scratch = append((*scratch)[:0], ready[:i]...)
   120  	if i > 0 {
   121  		m := copy(ready, ready[i:])
   122  		for i = m; i < n; i++ {
   123  			ready[i] = nil
   124  		}
   125  		workers.ready = ready[:m]
   126  	}
   127  	workers.lock.Unlock()
   128  
   129  	// Notify obsolete workers to stop.
   130  	// This notification must be outside the wp.lock, since ch.ch
   131  	// may be blocking and may consume a lot of time if many workers
   132  	// are located on non-local CPUs.
   133  	tmp := *scratch
   134  	for i, ch := range tmp {
   135  		ch.fcurTask <- nil
   136  		tmp[i] = nil
   137  	}
   138  }
   139  
   140  func (workers *GWorkers) getCh() *workerChan {
   141  	var ch *workerChan
   142  	createWorker := false
   143  
   144  	workers.lock.Lock()
   145  	ready := workers.ready
   146  	n := len(ready) - 1
   147  	if n < 0 {
   148  		if workers.workersCount < workers.fMaxWorkersCount {
   149  			createWorker = true
   150  			workers.workersCount++
   151  		}
   152  	} else {
   153  		ch = ready[n]
   154  		ready[n] = nil
   155  		workers.ready = ready[:n]
   156  	}
   157  	workers.lock.Unlock()
   158  
   159  	if ch == nil {
   160  		if !createWorker {
   161  			return nil
   162  		}
   163  		vch := workers.workerChanPool.Get()
   164  		if vch == nil {
   165  			vch = &workerChan{
   166  				fOwner:workers,
   167  				fcurTask: make(chan ITaskRunner, workerChanCap),
   168  			}
   169  		}
   170  		ch = vch.(*workerChan)
   171  		go func() {
   172  			workers.workerFunc(ch)
   173  			workers.workerChanPool.Put(vch)
   174  		}()
   175  	}
   176  	return ch
   177  }
   178  
   179  func (workers *GWorkers) release(ch *workerChan) bool {
   180  	ch.lastUseTime = CoarseTimeNow()
   181  	workers.lock.Lock()
   182  	if workers.mustStop {
   183  		workers.lock.Unlock()
   184  		return false
   185  	}
   186  	workers.ready = append(workers.ready, ch)
   187  	workers.lock.Unlock()
   188  	return true
   189  }
   190  
   191  
   192  func (workers *GWorkers) workerFunc(ch *workerChan) {
   193  	for curTask := range ch.fcurTask {
   194  		if curTask == nil {
   195  			break
   196  		}
   197  		curTask.Run() //执行
   198  		//回收curtask
   199  		switch runner := curTask.(type) {
   200  		case *defTaskRunner:
   201  			workers.deftaskrunnerPool.Put(runner)
   202  		}
   203  		if !workers.release(ch) {
   204  			break
   205  		}
   206  	}
   207  
   208  	workers.lock.Lock()
   209  	workers.workersCount--
   210  	workers.lock.Unlock()
   211  }
   212  
   213  func (workers *GWorkers)PostFunc(routineFunc GWorkerFunc,params ...interface{})  {
   214  	wch := workers.getCh()
   215  	if wch != nil {
   216  		taskrunner := workers.deftaskrunnerPool.Get().(*defTaskRunner)
   217  		taskrunner.runfunc = routineFunc
   218  		taskrunner.runargs = params
   219  		wch.fcurTask <- taskrunner
   220  	}
   221  }
   222  
   223  func (workers *GWorkers)Post(runner ITaskRunner)  {
   224  	wch := workers.getCh()
   225  	if wch != nil {
   226  		wch.fcurTask <- runner
   227  	}
   228  }
   229  
   230  func NewWorkers(maxGoroutinesAmount int, maxGoroutineIdleDuration time.Duration) *GWorkers {
   231  	gp := new(GWorkers)
   232  	gp.deftaskrunnerPool.New = func() interface{} {
   233  		return new(defTaskRunner)
   234  	}
   235  	if maxGoroutinesAmount <= 0 {
   236  		gp.fMaxWorkersCount = 256 * 1024
   237  	} else {
   238  		gp.fMaxWorkersCount = maxGoroutinesAmount
   239  	}
   240  	if maxGoroutineIdleDuration <= 0 {
   241  		gp.fMaxWorkerIdleTime = 15 * time.Second
   242  	} else {
   243  		gp.fMaxWorkerIdleTime = maxGoroutineIdleDuration
   244  	}
   245  	gp.Start()
   246  	return gp
   247  }
   248  
   249  var defWorkers *GWorkers
   250  
   251  func PostFunc(routineFunc GWorkerFunc,params ...interface{})  {
   252  	if defWorkers == nil{
   253  		defWorkers = NewWorkers(0,0)
   254  	}
   255  	defWorkers.PostFunc(routineFunc,params...)
   256  }
   257  
   258  func Post(runner ITaskRunner)  {
   259  	if defWorkers == nil{
   260  		defWorkers = NewWorkers(0,0)
   261  	}
   262  	defWorkers.Post(runner)
   263  }
   264  
   265  func StopWorkers()  {
   266  	if defWorkers != nil{
   267  		defWorkers.Stop()
   268  	}
   269  }