返回

Go 语言系列34:协程池

发布时间:2023-09-06 14:51:49 184

在其他语言中,为了减少线程频繁创建销毁带来的开销,通常我们会使用线程池来复用线程。在 Go 中, ​​goroutine​​ 是一个轻量级的线程,他的创建、调度都是在用户态进行,并不需要进入内核,这意味着创建销毁协程带来的开销是非常小的。因此,在大多数情况下是不太需要使用协程池的。但这一期我们还是讲一讲协程池,说不定哪天要用上了呢?


Go 语言系列34:协程池_复用

创建协程池结构体

Go 语言系列34:协程池_复用_02


第一步,我们定义一个 协程池(Pool) 结构体,其中包含两个属性。

type Pool struct {
work chan func() // 接收 task 任务
sem chan struct{} // 设置协程池大小 即可同时执行的协程数量
}

Go 语言系列34:协程池_复用

创建协程池对象函数

Go 语言系列34:协程池_复用_02


定义一个 ​​New​​ 函数,用于创建一个协程池对象。

// New 创建一个协程池对象 size 为协程池大小
func New(size int) *Pool {
return &Pool{
work: make(chan func()),
sem: make(chan struct{}, size),
}
}

Go 语言系列34:协程池_复用

执行任务函数

Go 语言系列34:协程池_复用_02


绑定一个执行任务的函数 ​​worker​​​ ,这里使用了 ​​for​​ 无限循环,使协程在执行完任务后,也不退出,而是一直在接收新的任务。

// worker 执行任务
func (p *Pool) worker(task func()) {
defer func() { <-p.sem }()
for {
task()
task = <-p.work
}
}

Go 语言系列34:协程池_复用

添加任务函数

Go 语言系列34:协程池_复用_02


绑定一个往协程池中添加任务的函数 ​​NewTask​​​ 。第一次调用 ​​NewTask​​​ 添加任务的时候,由于 ​​work​​​ 是无缓冲通道,所以会一定会走第二个 ​​case​​​ 的分支:使用 ​​go worker​​ 开启一个协程。

// NewTask 添加任务
func (p *Pool) NewTask(task func()) {
select {
case p.work <- task:
case p.sem <- struct{}{}:
go p.worker(task)
}
}

Go 语言系列34:协程池_复用

协程池的使用

Go 语言系列34:协程池_复用_02


下面是一个协程池的例子:

func main() {
pool := New(128)
pool.NewTask(func(){
fmt.Println("run task")
})
}

当然,这一期讲的也只是一个最最简单的实现,如果要了解更多关于协程池的知识,还请自行上网查阅相关资料。

参考文献:

[1] Alan A. A. Donovan; Brian W. Kernighan, Go 程序设计语言, Translated by 李道兵, 高博, 庞向才, 金鑫鑫 and 林齐斌, 机械工业出版社, 2017.

[2] https://mp.weixin.qq.com/s?__biz=MzAxMTA4Njc0OQ==&mid=2651439736&idx=5&sn=c7fc9e7f1315ed4d1a0aacc4a462d017&scene=19#wechat_redirect




特别声明:以上内容(图片及文字)均为互联网收集或者用户上传发布,本站仅提供信息存储服务!如有侵权或有涉及法律问题请联系我们。
举报
评论区(0)
按点赞数排序
用户头像
精选文章
thumb 中国研究员首次曝光美国国安局顶级后门—“方程式组织”
thumb 俄乌线上战争,网络攻击弥漫着数字硝烟
thumb 从网络安全角度了解俄罗斯入侵乌克兰的相关事件时间线
下一篇
进程的状态与转换 2023-09-06 12:12:44