素手烹茶|- 高性能低损耗的 Goroutine 池,Ants

Go语言最大的特色之一 , 就是其从语言的层面支持并发 。 Go语言使用了其特有的goroutine作为最基本的并发执行单元 , 以协程的方式 , 实现了更加轻量和高效的并发执行 。 然而 , goroutine缺乏一个高级的管理机制 , 原生情况下使用 , 要实现动态调整数量、内存资源复用、错误处理等 , 往往需要编写比较多的底层代码逻辑 。 Ants , 这个goroutine池实现 , 提供了对于大规模goroutine的管理功能 , 相比原生实现 , 资源使用率和执行性能都有了很大的提升 。
简介Ants , 是panjf2000在github上开源的高性能goroutine池 , 项目位于 , 目前版本为v2.4.0 。 Ants实现了对于大规模goroutine的调度管理和复用 , 允许使用者在开发Golang并发程序时限制goroutine数量 , 复用资源 , 达到更高效执行任务的效果 。 Ants提供了大量有用的接口 , 包括:任务提交、获取运行中的goroutine数量、动态调整池带下、释放和重启池等 。 Ants通过优秀的资源复用策略 , 极大地节省内存使用量 , 在大规模批量并发任务场景下 , 比原生的goroutine实现的并发具有更高的性能 。
安装Ants使用Go语言开发 , 需要Go1.8.x以上 。 Ants目前同时维护v1和v2版本 , 安装v1版本:
goget-ugithub.com/panjf2000/antsv2版本需要使用gomodule支持 , 开启GO111MODULE=on:
goget-ugithub.com/panjf2000/ants/v2
示例Ants对于任务的执行原理比较直观 , 通过一个工作池的形式维护goroutine集合 。 当向工作池提交任务时 , 从池中取出worker来执行 。 如果已经存在可用的goroutine了 , 那么直接开始执行 , 如果没有 , 则需要判断是否已经达到容量上限 。 如果还没有超过 , 那就意味着可用的worker比容量更少 , 此时启动新的worker来执行 。 而如果容量已经用完 , 就依据是否为阻塞模式 , 来马上返回 , 或是阻塞等待 。
当任务执行完毕 , 对应的worker就会得到释放 , 重新回到池中 , 等待下一个任务的调度 , 实现goroutine的复用 。
完整的工作池worker调度的逻辑和流程如下:
Ants支持不同的使用方式 , 可以直接使用Submit接口 , 使用默认配置的工作池完成任务执行 。 Submit函数的定义如下:
funcSubmit(taskfunc())error通过提供一个函数类型的任务参数 , 来把任务提交到工作池执行 。 我们来看一个简单的使用例子:
packagemainimport("fmt""sync""time""github.com/panjf2000/ants/v2")funcdemoFunc(){time.Sleep(10*time.Millisecond)fmt.Println("HelloWorld!")}funcmain(){deferants.Release()runTimes:=1000varwgsync.WaitGroupsyncCalculateSum:=func(){demoFunc()wg.Done()}fori:=0;i<runTimes;i++{wg.Add(1)_=ants.Submit(syncCalculateSum)}wg.Wait()fmt.Printf("runninggoroutines:%dn",ants.Running())fmt.Printf("finishalltasks.n")}在这个例子中 , 定义了一个简单的任务函数demoFunc , 短暂休眠后打印HelloWorld 。 在main函数中 , 使用了sync.WaitGroup来进行并发控制 , 把demoFunc包裹成为一个并发任务函数syncCalculateSum 。 我们要把这个任务执行1000次 , 就可以通过循环 , 进行1000次的ants.Submit调用 , 把所有任务都提交到工作池执行 。 提交完成后 , 等待任务完成 。 程序在完成了1000次的HelloWorld打印后 , 最终完成了任务执行 。