golang ants 源码分析

结构图

poolwithfunc与pool相差不大,这里我们只分析ants默认pool的流程

文件 作用
ants.go 定义常量、errors显示、默认建一个大小为2147483647的goroutine池、封装一些方便用户操作查看goroutine池的函数
options.go goroutine池的相关配置
pool.go 普通pool(不绑定特定函数)的创建以及对pool相关的操作
pool_func.go 创建绑定某个特定函数的pool以及对pool相关的操作
worker.go goworker的struct(其他语言中的类)、run(其他语言中的方法)
worker_array.go 一个worker_array的接口和一个能返回实现该接口的函数
worker_func.go
worker_loop_queue.go
worker_stack.go workerStack(struct)实现worker_array中的所有接口
spinlock.go 锁相关

关键结构

type Pool struct

type Pool struct {
capacity int32 // 容量
running int32 // 正在运行的数量
lock sync.Locker //定义一个锁 用以支持 Pool 的同步操作
workers workerArray // workers 一个接口 存放可循环利用的Work(goroutine)的相关信息
// type workerArray interface {
// len() int
// isEmpty() bool
// insert(worker *goWorker) error
// detach() *goWorker
// retrieveExpiry(duration time.Duration) []*goWorker
// reset()
// }
state int32 //记录池子的状态(关闭,开启)
cond *sync.Cond // 条件变量
workerCache sync.Pool // golang原始池子 使用sync.Pool对象池管理和创建worker对象,提升性能
blockingNum int // 阻塞等待的任务数量;
stopHeartbeat chan struct{} //一个空结构体的通道,仅用于接收标志
options *Options // 用于配置pool的options指针
}
  • func (p *Pool) purgePeriodically() //定期清理过期worker任务
  • func (p *Pool) Submit(task func()) error //提交func任务与worker绑定进行运行
  • func (p *Pool) Running() int //有多少个运行的worker
  • func (p *Pool) Free() int //返回空闲的worker数量
  • func (p *Pool) Cap() int // 返回pool的容量
  • ......
  • func (p *Pool) retrieveWorker() (w *goWorker) //返回一个worker

workerArray

type workerArray interface {
len() int // worker的数量
isEmpty() bool // worker是否为0
insert(worker *goWorker) error //将执行完的worker(goroutine)放回
detach() *goWorker // 获取worker
retrieveExpiry(duration time.Duration) []*goWorker //取出所有的过期 worker;
reset() // 重置
}

workerStack

type workerStack struct {
items []*goWorker //空闲的worker
expiry []*goWorker //过期的worker
size int
}

下面是对接口workerArray的实现

func (wq *workerStack) len() int {
return len(wq.items)
} func (wq *workerStack) isEmpty() bool {
return len(wq.items) == 0
} func (wq *workerStack) insert(worker *goWorker) error {
wq.items = append(wq.items, worker)
return nil
} //返回items中最后一个worker
func (wq *workerStack) detach() *goWorker {
l := wq.len()
if l == 0 {
return nil
}
w := wq.items[l-1]
wq.items[l-1] = nil // avoid memory leaks
wq.items = wq.items[:l-1] return w
} func (wq *workerStack) retrieveExpiry(duration time.Duration) []*goWorker {
n := wq.len()
if n == 0 {
return nil
} expiryTime := time.Now().Add(-duration) //过期时间=现在的时间-1s
index := wq.binarySearch(0, n-1, expiryTime) wq.expiry = wq.expiry[:0]
if index != -1 {
wq.expiry = append(wq.expiry, wq.items[:index+1]...) //因为以后进先出的模式去worker 所有过期的woker这样wq.items[:index+1]取
m := copy(wq.items, wq.items[index+1:])
for i := m; i < n; i++ { //m是存活的数量 下标为m之后的元素全部置为nil
wq.items[i] = nil
}
wq.items = wq.items[:m] //抹除后面多余的内容
}
return wq.expiry
} // 二分法查询过期的worker
func (wq *workerStack) binarySearch(l, r int, expiryTime time.Time) int {
var mid int
for l <= r {
mid = (l + r) / 2
if expiryTime.Before(wq.items[mid].recycleTime) {
r = mid - 1
} else {
l = mid + 1
}
}
return r
} func (wq *workerStack) reset() {
for i := 0; i < wq.len(); i++ {
wq.items[i].task <- nil //worker的任务置为nil
wq.items[i] = nil //worker置为nil
}
wq.items = wq.items[:0] //items置0
}

流程分析

创建pool

func NewPool(size int, options ...Option) (*Pool, error) {
opts := loadOptions(options...) // 导入配置
根据不同项进行配置此处省略 p := &Pool{
capacity: int32(size),
lock: internal.NewSpinLock(),
stopHeartbeat: make(chan struct{}, 1), //开一个通道用于接收一个停止标志
options: opts,
}
p.workerCache.New = func() interface{} {
return &goWorker{
pool: p,
task: make(chan func(), workerChanCap),
}
} p.workers = newWorkerArray(stackType, 0) p.cond = sync.NewCond(p.lock)
go p.purgePeriodically()
return p, nil
}

提交任务(将worker于func绑定)

func (p *Pool) retrieveWorker() (w *goWorker) {
spawnWorker := func() {
w = p.workerCache.Get().(*goWorker)
w.run()
} p.lock.Lock() w = p.workers.detach() // 获取列表中最后一个worker
if w != nil { // 取出来的话直接解锁
p.lock.Unlock()
} else if capacity := p.Cap(); capacity == -1 || capacity > p.Running() { //没取到但是容量为无限大或者容量未满
p.lock.Unlock()
spawnWorker() //开一个新的worker
} else { // 没取到 而且容量已经满了
if p.options.Nonblocking { //默认为False
p.lock.Unlock()
return
}
retry:
xxxx
goto retry
xxxx p.lock.Unlock()
}
return
}

goworker的运行

func (w *goWorker) run() {
w.pool.incRunning() //增加正在运行的worker数量
go func() {
defer func() {
w.pool.decRunning()
w.pool.workerCache.Put(w)
if p := recover(); p != nil {
if ph := w.pool.options.PanicHandler; ph != nil {
ph(p)
} else {
w.pool.options.Logger.Printf("worker exits from a panic: %v\n", p)
var buf [4096]byte
n := runtime.Stack(buf[:], false)
w.pool.options.Logger.Printf("worker exits from panic: %s\n", string(buf[:n]))
}
}
// Call Signal() here in case there are goroutines waiting for available workers.
w.pool.cond.Signal()
}() for f := range w.task { //阻塞接受task
if f == nil {
return
}
f() //执行函数
if ok := w.pool.revertWorker(w); !ok { // 将goworker放回items中
return
}
}
}()
}

go ants源码分析的更多相关文章

  1. ABP源码分析一:整体项目结构及目录

    ABP是一套非常优秀的web应用程序架构,适合用来搭建集中式架构的web应用程序. 整个Abp的Infrastructure是以Abp这个package为核心模块(core)+15个模块(module ...

  2. HashMap与TreeMap源码分析

    1. 引言     在红黑树--算法导论(15)中学习了红黑树的原理.本来打算自己来试着实现一下,然而在看了JDK(1.8.0)TreeMap的源码后恍然发现原来它就是利用红黑树实现的(很惭愧学了Ja ...

  3. nginx源码分析之网络初始化

    nginx作为一个高性能的HTTP服务器,网络的处理是其核心,了解网络的初始化有助于加深对nginx网络处理的了解,本文主要通过nginx的源代码来分析其网络初始化. 从配置文件中读取初始化信息 与网 ...

  4. zookeeper源码分析之五服务端(集群leader)处理请求流程

    leader的实现类为LeaderZooKeeperServer,它间接继承自标准ZookeeperServer.它规定了请求到达leader时需要经历的路径: PrepRequestProcesso ...

  5. zookeeper源码分析之四服务端(单机)处理请求流程

    上文: zookeeper源码分析之一服务端启动过程 中,我们介绍了zookeeper服务器的启动过程,其中单机是ZookeeperServer启动,集群使用QuorumPeer启动,那么这次我们分析 ...

  6. zookeeper源码分析之三客户端发送请求流程

    znode 可以被监控,包括这个目录节点中存储的数据的修改,子节点目录的变化等,一旦变化可以通知设置监控的客户端,这个功能是zookeeper对于应用最重要的特性,通过这个特性可以实现的功能包括配置的 ...

  7. java使用websocket,并且获取HttpSession,源码分析

    转载请在页首注明作者与出处 http://www.cnblogs.com/zhuxiaojie/p/6238826.html 一:本文使用范围 此文不仅仅局限于spring boot,普通的sprin ...

  8. ABP源码分析二:ABP中配置的注册和初始化

    一般来说,ASP.NET Web应用程序的第一个执行的方法是Global.asax下定义的Start方法.执行这个方法前HttpApplication 实例必须存在,也就是说其构造函数的执行必然是完成 ...

  9. ABP源码分析三:ABP Module

    Abp是一种基于模块化设计的思想构建的.开发人员可以将自定义的功能以模块(module)的形式集成到ABP中.具体的功能都可以设计成一个单独的Module.Abp底层框架提供便捷的方法集成每个Modu ...

随机推荐

  1. @Bean和@Componet区别

    无意在两个类上看到了这两个注解,一个使用了@Bean配合@Configuration,一个使用了@Componet.依稀记得这两个注解都是实现以前在xml中<bean xxx/>的功能,但 ...

  2. Effective Java —— 消除过期的对象引用

    本文参考 本篇文章参考自<Effective Java>第三版第七条"Eliminate obsolete object references" Memory leak ...

  3. 1.时任务XXL_Job框架踩过的坑

    遇到的问题 问题1:执行器地址为空 原因-->执行器中 没有地址 解决方案-->输入地址:http://IP地址:端口 IP地址 端口 问题2:异常信息unknown code for r ...

  4. 【freertos】007-系统节拍和系统延时管理实现细节

    前言 本章节的时钟系统节拍主要分析FreeRTOS内核相关及北向接口层,南向接口层不分析. 本章节的系统延时主要分析任务系统延时实现. 原文:李柱明博客:https://www.cnblogs.com ...

  5. C++中初始化列表的使用(总结)

    原文链接 https://www.cnblogs.com/dishengAndziyu/p/10906081.html 参考链接:https://www.cnblogs.com/laiqun/p/57 ...

  6. 二、cadence焊盘与封装制作操作步骤详细说明

    一.焊盘制作 1.打开Pad Designer软件,新建文件--设置保存路径和焊盘名称(规范命名) 2.Parameters--设置单位--过孔类型--是否镀金 3.Layers--single la ...

  7. Spring Security OAuth 笔记

    1  单点登录 关于单点登录的原理,我觉得下面这位老哥讲的比较清楚,有兴趣可以看一下,下面我把其中的重点在此做个笔记总结 https://juejin.cn/post/6844904079274197 ...

  8. CSS 3-浮动、定位

    文档流 文档流是一种默认定位方式,在文档流中元素框的位置由元素在html中的位置决定,文档流中元素的position属性为默认的static或继承来的static并按照普通流定位.块级元素独占一行,自 ...

  9. 程序人生:织梦dedecms后台/会员验证码关闭

    dedecms默认是所有的功能几乎只要用到验证码的地方我们都需要验证的,如果要关闭一些验证功能我们可以参考下面的教程,这里介绍了关闭后台,留言板,会员系统等验证码功能关闭了.提示:支持DedeCMS ...

  10. 探索前端黑科技——通过 png 图的 rgba 值缓存数据

    本文系原创,欢迎转载,转载请注明作者信息项目地址:SphinxJS在线体验地址:https://jrainlau.github.io/sp... 说起前端缓存,大部分人想到的无非是几个常规的方案,比如 ...