Golang(九)简单 Goroutine 池实现
0. 前言
- 最近使用 Golang 写一个并发执行的测试脚本
- 之前习惯使用 Java,习惯性想先建一个线程池。然后意识到 Golang 没有封装好的线程池
- 结合之前学习的 Goroutine 原理和 Golang 大道至简的设计思想,可能 Goroutine 的开销和切换代价比较低,不需要对并发数有过多限制
- 但是 Goroutine 启动数量过多的话总感觉不太好,于是利用锁和通道实现了简单的线程池做并发控制,欢迎大家点评
- 源码地址:https://github.com/wangao1236/GoPool
1. 相关接口
- 接口仿照 Java 的 ExecutorService 和 Runnable 接口定义:
// Task represents an in-process Goroutine task.
type Task interface {
// Run method corresponds to Run method of Java's Runnable interface.
Run()
} // Executor defines the actions associated with the Goroutine pool.
type Executor interface {
// Execute method corresponds to Execute method of Java's ExecutorService interface.
Execute(task Task)
// Wait waits for all the tasks to complete.
Wait()
// Done returns a channel which is closed if all the tasks completed.
Done() chan struct{}
}
- 接口调用:
func TestNewExecutor(t *testing.T) {
t.Log(t.Name())
ex := NewExecutor() for _, domain := range domains {
ex.Execute(&TestTask{
fmt.Sprintf("ping %s -c 10", domain),
})
}
ex.Wait()
}
- 首先定义一个 Executor,然后通过 Execute 传入 Task 对象,调用 Wait 方法等待所有任务结束
2. 具体实现
- 主要利用 sync.Mutex 和 []channel struct{} 维护一个等待执行的任务队列
- 任务传入时,等待一个 startCh 通道信号
- 对于符合执行条件(未设定并发数或者当前执行任务小于并发数时)关闭 startCh 信号,解除阻塞
- 任务执行完毕后,关闭 stopCh 信号,允许等待队列里的任务继续执行
- 对于所有任务执行完毕(正在执行数和等待执行数均为 0),关闭 done 信号,解除整个 Executor 的阻塞,表示所有任务执行完毕
- 部分代码如下:
package pkg import (
"sync"
) // Task represents an in-process Goroutine task.
type Task interface {
// Run method corresponds to Run method of Java's Runnable interface.
Run()
} // Executor defines the actions associated with the Goroutine pool.
type Executor interface {
// Execute method corresponds to Execute method of Java's ExecutorService interface.
Execute(task Task)
// Wait waits for all the tasks to complete.
Wait()
// Done returns a channel which is closed if all the tasks completed.
Done() chan struct{}
} type executor struct {
lock sync.Mutex
waitingTasks []chan struct{}
activeTasks int64
concurrencyLimit int64
done chan struct{}
} func (ex *executor) Execute(task Task) {
ex.start(task)
} func (ex *executor) Wait() {
<-ex.done
} func (ex *executor) Done() chan struct{} {
return ex.done
} func (ex *executor) start(task Task) {
startCh := make(chan struct{})
stopCh := make(chan struct{}) go startTask(startCh, stopCh, task)
ex.enqueue(startCh)
go ex.waitDone(stopCh) } // NewExecutor returns a new Executor.
func NewExecutor(concurrencyLimit int64) Executor {
ex := &executor{
waitingTasks: make([]chan struct{}, ),
activeTasks: ,
concurrencyLimit: concurrencyLimit,
done: make(chan struct{}),
}
return ex
} func startTask(startCh, stopCh chan struct{}, task Task) {
defer close(stopCh) <-startCh
task.Run()
} func (ex *executor) enqueue(startCh chan struct{}) {
ex.lock.Lock()
defer ex.lock.Unlock() if ex.concurrencyLimit == || ex.activeTasks < ex.concurrencyLimit {
close(startCh)
ex.activeTasks++
} else {
ex.waitingTasks = append(ex.waitingTasks, startCh)
}
} func (ex *executor) waitDone(stopCh chan struct{}) {
<-stopCh ex.lock.Lock()
defer ex.lock.Unlock() if len(ex.waitingTasks) == {
ex.activeTasks--
if ex.activeTasks == {
close(ex.done)
}
} else {
close(ex.waitingTasks[])
ex.waitingTasks = ex.waitingTasks[:]
}
}
- 通过 Executor 传入的任务首先开始执行 start 方法
- start 方法里定义了该任务的 startCh 和 stopCh 信号
- 各启动一个 Goroutine 等待任务开始和任务结束
- 同时把表示任务的 startCh 加入等待队列中表示,队列需要靠 sync.Mutex 保护
- 当一个任务结束时,解除 waitDone 方法的阻塞,启动队首的任务,解除 startTask 里的阻塞
- 所有任务结束后,解除 Wait 方法里的阻塞
- 完整代码参见上述链接
Golang(九)简单 Goroutine 池实现的更多相关文章
- golang中goroutine池的使用
1. 概念本质上是生产者.消费者模型可以有效的控制goroutine数量,防止暴涨案例:生成一个随机数,计算该随机数每一个数字相加的和,例如:123:1+2+3=6主协程负责生产数据发送到待处理通道中 ...
- Golang之chan/goroutine(转)
原文地址:http://tchen.me/posts/2014-01-27-golang-chatroom.html?utm_source=tuicool&utm_medium=referra ...
- gf框架之grpool - 高性能的goroutine池
Go语言中的goroutine虽然相对于系统线程来说比较轻量级,但是在高并发量下的goroutine频繁创建和销毁对于性能损耗以及GC来说压力也不小.充分将goroutine复用,减少goroutin ...
- 菜鸟nginx源代码剖析数据结构篇(九) 内存池ngx_pool_t
菜鸟nginx源代码剖析数据结构篇(九) 内存池ngx_pool_t Author:Echo Chen(陈斌) Email:chenb19870707@gmail.com Blog:Blog.csdn ...
- 菜鸟nginx源码剖析数据结构篇(九) 内存池ngx_pool_t[转]
菜鸟nginx源码剖析数据结构篇(九) 内存池ngx_pool_t Author:Echo Chen(陈斌) Email:chenb19870707@gmail.com Blog:Blog.csdn. ...
- [Golang] 一个简易代理池
晚上写了一个代理池,就是在一个代理网站上爬取代理ip和端口以及测试是否可用.接下来可能考虑扩展成一个比较大的 golang实现的代理池. 简易版代码: package main import ( &q ...
- Linux多线程实践(9) --简单线程池的设计与实现
线程池的技术背景 在面向对象编程中,创建和销毁对象是很费时间的,因为创建一个对象要获取内存资源或者其它更多资源.在Java中更是如此,虚拟机将试图跟踪每一个对象,以便能够在对象销毁后进行垃圾回收.所以 ...
- Linux下简单线程池的实现
大多数的网络服务器,包括Web服务器都具有一个特点,就是单位时间内必须处理数目巨大的连接请求,但是处理时间却是比较短的.在传统的多线程服务器模型中是这样实现的:一旦有个服务请求到达,就创建一个新的服务 ...
- Golang设计模式—简单工厂模式(Simple Factory Pattern)
Golang设计模式--简单工厂模式 背景 假设我们在做一款小型翻译软件,软件可以将德语.英语.日语都翻译成目标中文,并显示在前端. 思路 我们会有三个具体的语言翻译结构体,或许以后还有更多,但现在分 ...
随机推荐
- java架构之路-(tomcat网络模型)简单聊聊tomcat(一)
tomcat使我们熟知的也是我们使用最多的web服务器了,至少我是使用最多的.常见的web服务器还有Apache,web logic,JBOSS等,对于tomcat的安装我就不再赘述了,简单的不能再简 ...
- Zabbix触发器和监控项设置时间范围
目录 一.实际业务场景 业务问题 解决办法 二.Zabbix触发器和监控项与用户预警设置时间范围配置流程 一.触发器设置时间范围 二.监控项设置时间范围 三.用户报警设置启用时间 一.实际业务场景 业 ...
- C# 三元表达式
一.背景 因编程的基础差,因此最近开始巩固学习C#基础,后期把自己学习的东西,总结相应文章中,有不足处请大家多多指教. 二.语法 表达式1?表达式2:表达式3 描述: 表达式1一般为一个关系表达式. ...
- 纯 JS 设置文本框的默认提示
HTML5 中有个新特性叫 placeholder,一般用它来描述输入字段的预期值,适用于 text.search.password 等类型的 input 以及 textarea.示例如下: < ...
- 海关单一窗口程序出现网络/MQ问题后自动修复处理
单一窗口切换了2年多了,由于RabbitMQ或者网络的不稳定,或者升级或者网络调整,等等诸多问题导致了海关单一窗口程序会不定期的出现文件及回执自动处理的作业停止的问题. 最近终于想明白了,把日志监控起 ...
- Java生鲜电商平台-源码地址公布与思考和建议
Java生鲜电商平台-源码地址公布与思考和建议 说明:今天是承诺给大家的最后一天,我公布了github地址(QQ群里面有).诚然这个是我的计划中的事情,但是有以下几点思考请大家共勉: 1. 你下了那么 ...
- Django的路由系统:URL
一. URLconf配置 基本格式 from django.conf.urls import url urlpatterns = [ url(正则表达式, views视图,参数,别名), ] 参数说明 ...
- 三层架构介绍和MVC设计模型介绍
springmvc是什么? Spring Web MVC是一种基于Java的实现了Web MVC设计模式的请求驱动类型的轻量级Web框架,即使用了MVC架构模式的思想,将web层 进行职责解耦,基于请 ...
- Linux Kernel PANIC(三)--Soft Panic/Oops调试及实例分析【转】
转自:https://blog.csdn.net/gatieme/article/details/73715860 版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原 ...
- Win7共享只看到部分文件
把192.168.70.88这台机器的Android目录做为共享文件夹. 共享端:windows server 用户端:其它pc机安装windows7 或windows 10 在用户端只能看到部分文件 ...