当前位置: 首页 > news >正文

gf框架之grpool - 高性能的goroutine池

Go语言中的goroutine虽然相对于系统线程来说比较轻量级,但是在高并发量下的goroutine频繁创建和销毁对于性能损耗以及GC来说压力也不小。充分将goroutine复用,减少goroutine的创建/销毁的性能损耗,这便是grpool对goroutine进行池化封装的目的。例如,针对于100W个执行任务,使用goroutine的话需要不停创建并销毁100W个goroutine,而使用grpool也许底层只需要几千个goroutine便能充分复用地执行完成所有任务。经测试,在高并发下grpool的性能比原生的goroutine高出几倍到数百倍!并且随之也极大地降低了内存使用率。

性能测试报告:http://johng.cn/grpool-performance/

方法列表

func Add(f func()) func Jobs() int func SetExpire(expire int) func SetSize(size int) func Size() int type Pool func New(expire int, sizes ...int) *Pool func (p *Pool) Add(f func()) func (p *Pool) Close() func (p *Pool) Jobs() int func (p *Pool) SetExpire(expire int) func (p *Pool) SetSize(size int) func (p *Pool) Size() int 

通过grpool.New方法创建一个goroutine池,并给定池中goroutine的有效时间,单位为,第二个参数为非必需参数,用于限定池中的工作goroutine数量,默认为不限制。需要注意的是,任务可以不停地往池中添加,没有限制,但是工作的goroutine是可以做限制的。我们可以通过Size()方法查询当前的工作goroutine数量,使用Jobs()方法查询当前池中待处理的任务数量。同时,池的大小和goroutine有效期可以通过SetSize和SetExpire方法在运行时进行动态改变。

同时,为便于使用,grpool包提供了默认的goroutine池,直接通过grpool.Add即可往默认的池中添加任务,任务参数必须是一个 func()类型的函数/方法。

使用示例

1、使用默认的goroutine池,限制10个工作goroutine执行1000个任务。

https://gitee.com/johng/gf/blob/master/geg/os/grpool/grpool1.go

package main

import (
    "time"
    "fmt" "gitee.com/johng/gf/g/os/gtime" "gitee.com/johng/gf/g/os/grpool" ) func job() { time.Sleep(1*time.Second) } func main() { grpool.SetSize(10) for i := 0; i < 1000; i++ { grpool.Add(job) } gtime.SetInterval(2*time.Second, func() bool { fmt.Println("size:", grpool.Size()) fmt.Println("jobs:", grpool.Jobs()) return true }) select {} } 

这段程序中的任务函数的功能是sleep 1秒钟,这样便能充分展示出goroutine数量限制功能。其中,我们使用了gtime.SetInterval定时器每隔2秒钟打印出当前默认池中的工作goroutine数量以及待处理的任务数量。

2、我们再来看一个新手经常容易出错的例子

https://gitee.com/johng/gf/blob/master/geg/os/grpool/grpool2.go

package main

import (
    "fmt"
    "sync" "gitee.com/johng/gf/g/os/grpool" ) func main() { wg := sync.WaitGroup{} for i := 0; i < 10; i++ { wg.Add(1) grpool.Add(func() { fmt.Println(i) wg.Done() }) } wg.Wait() } 

我们这段代码的目的是要顺序地打印出0-9,然而运行后却输出:

10
10
10
10
10
10
10
10
10
10

为什么呢?这里的执行结果无论是采用go关键字来执行还是grpool来执行都是如此。原因是,对于异步线程/协程来讲,函数进行进行异步执行注册时,该函数并未真正开始执行(注册时只在goroutine的栈中保存了变量i的内存地址),而一旦开始执行时函数才会去读取变量i的值,而这个时候变量i的值已经自增到了10。 清楚原因之后,改进方案也很简单了,就是在注册异步执行函数的时候,把当时变量i的值也一并传递获取;或者把当前变量i的值赋值给一个不会改变的临时变量,在函数中使用该临时变量而不是直接使用变量i。

改进后的示例代码如下:

1)、使用go关键字

https://gitee.com/johng/gf/blob/master/geg/os/grpool/grpool3.go

package main

import (
    "fmt"
    "sync" ) func main() { wg := sync.WaitGroup{} for i := 0; i < 10; i++ { wg.Add(1) go func(v int){ fmt.Println(v) wg.Done() }(i) } wg.Wait() } 

执行后,输出结果为:

9
0
1
2
3
4
5
6
7
8

注意,异步执行时并不会保证按照函数注册时的顺序执行,以下同理。

2)、使用临时变量

https://gitee.com/johng/gf/blob/master/geg/os/grpool/grpool4.go

package main

import (
    "fmt"
    "sync" "gitee.com/johng/gf/g/os/grpool" ) func main() { wg := sync.WaitGroup{} for i := 0; i < 10; i++ { wg.Add(1) v := i grpool.Add(func() { fmt.Println(v) wg.Done() }) } wg.Wait() } 

执行后,输出结果为:

9
0
1
2
3
4
5
6
7
8

这里可以看到,使用grpool进行任务注册时,只能使用func()类型的参数,因此无法在任务注册时把变量i的值注册进去,因此只能采用临时变量的形式来传递当前变量i的值。

阅读原文

转载于:https://www.cnblogs.com/276815076/p/8416652.html

相关文章:

  • 谷歌浏览器如何调试JS
  • CocosCreator引擎修改与定制
  • 新年的展望,2018 hello world~
  • Collection---CopyOnWrite(应用于大量度 而少量写的场景)
  • 模块使用
  • 16、sockect
  • USACO 2006 NOV Corn Fields
  • 存储快照实现原理
  • 软件需求模式阅读笔记1
  • centos6之前版本的启动流程
  • django 之 用户忘记密码的解决办法
  • 青蛙跳杯子
  • 详解mysql 获取当前日期及格式化
  • 事务、视图、索引、备份、还原
  • 软件工程——个人记录(g)
  • Angular2开发踩坑系列-生产环境编译
  • AWS实战 - 利用IAM对S3做访问控制
  • CEF与代理
  • Eureka 2.0 开源流产,真的对你影响很大吗?
  • Hibernate最全面试题
  • JavaWeb(学习笔记二)
  • Java小白进阶笔记(3)-初级面向对象
  • JS学习笔记——闭包
  • open-falcon 开发笔记(一):从零开始搭建虚拟服务器和监测环境
  • SegmentFault 技术周刊 Vol.27 - Git 学习宝典:程序员走江湖必备
  • Yii源码解读-服务定位器(Service Locator)
  • 从重复到重用
  • 看域名解析域名安全对SEO的影响
  • 猫头鹰的深夜翻译:JDK9 NotNullOrElse方法
  • 数据库写操作弃用“SELECT ... FOR UPDATE”解决方案
  • Spring第一个helloWorld
  • 测评:对于写作的人来说,Markdown是你最好的朋友 ...
  • 积累各种好的链接
  • 资深实践篇 | 基于Kubernetes 1.61的Kubernetes Scheduler 调度详解 ...
  • #if 1...#endif
  • #pragma once
  • (17)Hive ——MR任务的map与reduce个数由什么决定?
  • (2)nginx 安装、启停
  • (附源码)计算机毕业设计SSM疫情居家隔离服务系统
  • (原創) 如何刪除Windows Live Writer留在本機的文章? (Web) (Windows Live Writer)
  • ... fatal error LINK1120:1个无法解析的外部命令 的解决办法
  • .CSS-hover 的解释
  • .net mvc actionresult 返回字符串_.NET架构师知识普及
  • .NET 实现 NTFS 文件系统的硬链接 mklink /J(Junction)
  • .NET中的十进制浮点类型,徐汇区网站设计
  • @configuration注解_2w字长文给你讲透了配置类为什么要添加 @Configuration注解
  • [2016.7 day.5] T2
  • [Android] Implementation vs API dependency
  • [ArcPy百科]第三节: Geometry信息中的空间参考解析
  • [BZOJ1053][HAOI2007]反素数ant
  • [i.MX]飞思卡尔IMX6处理器的GPIO-IOMUX_PAD说明
  • [IE编程] IE 是如何决定Accept-Language 属性的
  • [JavaScript]如何讓IE9, IE8, IE7, IE6關閉視窗時不彈出對話訊息
  • [Linux] LVS+Keepalived高可用集群部署
  • [Linux打怪升级之路]-信号的保存和递达