github.com/suiyunonghen/DxCommonLib@v0.5.3/GWorkers.go (about)

     1  /*
     2  从fasthttp中变更过来的GoRoutine池
     3  Autor: 不得闲
     4  QQ:75492895
     5  */
     6  package DxCommonLib
     7  
     8  import (
     9  	_ "go.uber.org/automaxprocs"
    10  	"runtime"
    11  	"sync"
    12  	"time"
    13  )
    14  
    15  type (
    16  	GWorkerFunc func(data ...interface{})
    17  	GWorkers    struct {
    18  		mustStop           bool
    19  		fMaxWorkersCount   int //能同时存在的最多线程个数
    20  		workersCount       int
    21  		fMaxWorkerIdleTime time.Duration //线程能空闲的最长时间,超过这个时间了会回收掉这个线程
    22  		ready              []*workerChan //准备好的空闲线程
    23  		lock               sync.Mutex
    24  		workerChanPool     sync.Pool //工作者
    25  		fStopChan          chan struct{}
    26  	}
    27  
    28  	workerChan struct {
    29  		lastUseTime time.Time
    30  		fOwner      *GWorkers
    31  		fCurTask    chan defTaskRunner //ITaskRunner
    32  	}
    33  
    34  	defTaskRunner struct {
    35  		runFunc GWorkerFunc
    36  		runArgs []interface{}
    37  	}
    38  )
    39  
    40  func (workers *GWorkers) Start() {
    41  	if workers.fStopChan != nil {
    42  		panic("BUG: GWorkers already started")
    43  	}
    44  	workers.fStopChan = make(chan struct{})
    45  	stopCh := workers.fStopChan
    46  	workers.workerChanPool.New = func() interface{} {
    47  		return &workerChan{
    48  			fOwner:   workers,
    49  			fCurTask: make(chan defTaskRunner, workerChanCap),
    50  		}
    51  	}
    52  	go func() {
    53  		var scratch []*workerChan
    54  		for {
    55  			select {
    56  			case <-stopCh:
    57  				return
    58  			case <-After(workers.fMaxWorkerIdleTime):
    59  				//执行上了
    60  				workers.clean(&scratch) //定时执行清理回收线程
    61  			}
    62  		}
    63  	}()
    64  }
    65  
    66  func (workers *GWorkers) Stop() {
    67  	if workers.fStopChan == nil {
    68  		panic("BUG: GWorkers wasn't started")
    69  	}
    70  	close(workers.fStopChan)
    71  	workers.fStopChan = nil
    72  
    73  	// Stop all the workers waiting for incoming connections.
    74  	// Do not wait for busy workers - they will stop after
    75  	// serving the connection and noticing wp.mustStop = true.
    76  	workers.lock.Lock()
    77  	ready := workers.ready
    78  	l := len(ready)
    79  	for i := 0; i < l; i++ {
    80  		ready[i].fCurTask <- defTaskRunner{}
    81  		ready[i] = nil
    82  	}
    83  	workers.ready = ready[:0]
    84  	workers.mustStop = true
    85  	workers.lock.Unlock()
    86  }
    87  
    88  var workerChanCap = func() int {
    89  	// Use blocking workerChan if GOMAXPROCS=1.
    90  	// This immediately switches Serve to WorkerFunc, which results
    91  	// in higher performance (under go1.5 at least).
    92  	if runtime.GOMAXPROCS(0) == 1 {
    93  		return 0
    94  	}
    95  
    96  	// Use non-blocking workerChan if GOMAXPROCS>1,
    97  	// since otherwise the Serve caller (Acceptor) may lag accepting
    98  	// new connections if WorkerFunc is CPU-bound.
    99  	return 1
   100  }()
   101  
   102  func (workers *GWorkers) clean(scratch *[]*workerChan) {
   103  	// Clean least recently used workers if they didn't serve connections
   104  	// for more than maxIdleWorkerDuration.
   105  	criticalTime := time.Now().Add(-workers.fMaxWorkerIdleTime)
   106  	workers.lock.Lock()
   107  	ready := workers.ready
   108  	n := len(ready)
   109  	//超过设定的最大空闲时间的,就解雇掉
   110  	// Use binary-search algorithm to find out the index of the least recently worker which can be cleaned up.
   111  	l, r, mid := 0, n-1, 0
   112  	for l <= r {
   113  		mid = (l + r) / 2
   114  		if criticalTime.After(workers.ready[mid].lastUseTime) {
   115  			l = mid + 1
   116  		} else {
   117  			r = mid - 1
   118  		}
   119  	}
   120  	if r == -1 {
   121  		workers.lock.Unlock()
   122  		return
   123  	}
   124  	i := r
   125  	*scratch = append((*scratch)[:0], ready[:i+1]...)
   126  	m := copy(ready, ready[i+1:])
   127  	for i = m; i < n; i++ {
   128  		ready[i] = nil
   129  	}
   130  	workers.ready = ready[:m]
   131  	workers.lock.Unlock()
   132  
   133  	// Notify obsolete workers to stop.
   134  	// This notification must be outside the wp.lock, since ch.ch
   135  	// may be blocking and may consume a lot of time if many workers
   136  	// are located on non-local CPUs.
   137  	tmp := *scratch
   138  	for i = 0; i < len(tmp); i++ {
   139  		tmp[i].fCurTask <- defTaskRunner{}
   140  		tmp[i] = nil
   141  	}
   142  }
   143  
   144  func (workers *GWorkers) getCh() *workerChan {
   145  	var ch *workerChan
   146  	createWorker := false
   147  
   148  	workers.lock.Lock()
   149  	ready := workers.ready
   150  	n := len(ready) - 1
   151  	if n < 0 {
   152  		if workers.workersCount < workers.fMaxWorkersCount {
   153  			createWorker = true
   154  			workers.workersCount++
   155  		}
   156  	} else {
   157  		ch = ready[n]
   158  		ready[n] = nil
   159  		workers.ready = ready[:n]
   160  	}
   161  	workers.lock.Unlock()
   162  
   163  	if ch == nil {
   164  		if !createWorker {
   165  			return nil
   166  		}
   167  		vch := workers.workerChanPool.Get()
   168  		ch = vch.(*workerChan)
   169  		go func() {
   170  			workers.workerFunc(ch)
   171  			workers.workerChanPool.Put(vch)
   172  		}()
   173  	}
   174  	return ch
   175  }
   176  
   177  func (workers *GWorkers) release(ch *workerChan) bool {
   178  	ch.lastUseTime = time.Now()
   179  	workers.lock.Lock()
   180  	if workers.mustStop {
   181  		workers.lock.Unlock()
   182  		return false
   183  	}
   184  	workers.ready = append(workers.ready, ch)
   185  	workers.lock.Unlock()
   186  	return true
   187  }
   188  
   189  func (workers *GWorkers) workerFunc(ch *workerChan) {
   190  	//waitTimes := workers.fMaxWorkerIdleTime + time.Second * 5
   191  	for {
   192  		curTask := <-ch.fCurTask
   193  		if curTask.runFunc == nil {
   194  			break
   195  		}
   196  		curTask.runFunc(curTask.runArgs...)
   197  		if !workers.release(ch) {
   198  			break
   199  		}
   200  		/*select {
   201  		case curTask := <-ch.fCurTask:
   202  			if curTask.runFunc == nil {
   203  				break
   204  			}
   205  			curTask.runFunc(curTask.runArgs...)
   206  			if !workers.release(ch) {
   207  				break
   208  			}
   209  		case <-After(waitTimes):
   210  			//这么长时间都没有等到信号,是不是已经跪了
   211  			//删除这个长期占有的channel
   212  			workers.lock.Lock()
   213  			n := len(workers.ready)
   214  			for i := n - 1;i>=0;i--{
   215  				if workers.ready[i] == ch{
   216  					if i == n - 1{
   217  						workers.ready = workers.ready[:i]
   218  					}else{
   219  						workers.ready = append(workers.ready[:i],workers.ready[i+1:]...)
   220  					}
   221  					workers.workersCount--
   222  					break
   223  				}
   224  			}
   225  			workers.lock.Unlock()
   226  			return
   227  		}*/
   228  	}
   229  	workers.lock.Lock()
   230  	workers.workersCount--
   231  	workers.lock.Unlock()
   232  }
   233  
   234  func (workers *GWorkers) PostFunc(routineFunc GWorkerFunc, params ...interface{}) bool {
   235  	wch := workers.getCh()
   236  	if wch != nil {
   237  		wch.fCurTask <- defTaskRunner{
   238  			runFunc: routineFunc,
   239  			runArgs: params,
   240  		}
   241  		return true
   242  	}
   243  	return false
   244  }
   245  
   246  // MustPostFunc 必然投递
   247  func (workers *GWorkers) MustPostFunc(routineFunc GWorkerFunc, params ...interface{}) {
   248  	for {
   249  		wch := workers.getCh()
   250  		if wch != nil {
   251  			wch.fCurTask <- defTaskRunner{
   252  				runFunc: routineFunc,
   253  				runArgs: params,
   254  			}
   255  			return
   256  		}
   257  		runtime.Gosched()
   258  	}
   259  }
   260  
   261  // MustRunAsync 必须异步执行到
   262  func (workers *GWorkers) MustRunAsync(routineFunc GWorkerFunc, params ...interface{}) {
   263  	for i := 0; i < 10; i++ {
   264  		wch := workers.getCh()
   265  		if wch != nil {
   266  			wch.fCurTask <- defTaskRunner{
   267  				runFunc: routineFunc,
   268  				runArgs: params,
   269  			}
   270  			return
   271  		}
   272  		runtime.Gosched()
   273  	}
   274  	go routineFunc(params...)
   275  }
   276  
   277  func (workers *GWorkers) TryPostAndRun(routineFunc GWorkerFunc, params ...interface{}) {
   278  	for idx := 0; idx < 10; idx++ {
   279  		wch := workers.getCh()
   280  		if wch != nil {
   281  			wch.fCurTask <- defTaskRunner{
   282  				runFunc: routineFunc,
   283  				runArgs: params,
   284  			}
   285  			return
   286  		}
   287  		runtime.Gosched()
   288  	}
   289  	routineFunc(params...)
   290  	return
   291  }
   292  
   293  func NewWorkers(maxGoroutinesAmount int, maxGoroutineIdleDuration time.Duration) *GWorkers {
   294  	gp := new(GWorkers)
   295  	if maxGoroutinesAmount <= 0 {
   296  		gp.fMaxWorkersCount = 512 * 1024
   297  	} else {
   298  		gp.fMaxWorkersCount = maxGoroutinesAmount
   299  	}
   300  	if maxGoroutineIdleDuration <= 0 {
   301  		gp.fMaxWorkerIdleTime = 10 * time.Second
   302  	} else {
   303  		gp.fMaxWorkerIdleTime = maxGoroutineIdleDuration
   304  	}
   305  	gp.Start()
   306  	return gp
   307  }
   308  
   309  var defWorkers *GWorkers
   310  
   311  func ResetDefaultWorker(maxGoroutinesAmount int, maxGoroutineIdleDuration time.Duration) {
   312  	if defWorkers != nil {
   313  		defWorkers.Stop()
   314  	}
   315  	defWorkers = NewWorkers(maxGoroutinesAmount, maxGoroutineIdleDuration)
   316  }
   317  
   318  func PostFunc(routineFunc GWorkerFunc, params ...interface{}) bool {
   319  	if defWorkers == nil {
   320  		defWorkers = NewWorkers(0, 0)
   321  	}
   322  	return defWorkers.PostFunc(routineFunc, params...)
   323  }
   324  
   325  func MustPostFunc(routineFunc GWorkerFunc, params ...interface{}) {
   326  	if defWorkers == nil {
   327  		defWorkers = NewWorkers(0, 0)
   328  	}
   329  	defWorkers.MustPostFunc(routineFunc, params...)
   330  }
   331  
   332  func TryPostAndRun(routineFunc GWorkerFunc, params ...interface{}) {
   333  	if defWorkers == nil {
   334  		defWorkers = NewWorkers(0, 0)
   335  	}
   336  	defWorkers.TryPostAndRun(routineFunc, params...)
   337  }
   338  
   339  // MustRunAsync 必须异步执行到
   340  func MustRunAsync(routineFunc GWorkerFunc, params ...interface{}) {
   341  	if defWorkers == nil {
   342  		defWorkers = NewWorkers(0, 0)
   343  	}
   344  	defWorkers.MustRunAsync(routineFunc, params...)
   345  }
   346  
   347  func StopWorkers() {
   348  	if defWorkers != nil {
   349  		defWorkers.Stop()
   350  	}
   351  }