【24小时创业】2022年8月18日
06-17
经常写Go的朋友都知道,Go语言的goruntine对于这种编程语言来说是一个很棒的工具。与线程相比,基于协程的goruntine更加轻量高效,语法也非常简单。
为什么协程比线程更高效?协程和线程是两种不同的并发执行控制结构,它们在很多方面都有显着的不同。首先,从定义上来说,协程是程序级的执行单元,而且是轻量级的。
线程是操作系统级别的并发执行单元,是重量级的。协程的堆栈是动态的,可以根据需要增长和收缩,并且内存使用高效。
线程有自己固定大小的堆栈,堆栈大小限制了线程的数量,并可能导致堆栈溢出错误。协程的创建和切换成本非常低,因为它们是在用户级别进行管理的,不需要系统级别的上下文切换。
线程的创建和切换成本比较高,因为它们是由操作系统管理的,并且涉及到系统级的上下文切换。举个例子,如果你想把钱转到一个人的银行卡账户上,那么协程就像一张电子(在线)银行卡,而线程就像一台ATM机。
如果您使用网银转账,只需动动手指,即可绑定某宝或某信。可以转账,不过去ATM机会比较麻烦,但本质上最终的转账都会通过银行的信息系统完成,只是不同方式的运营成本不同。
协程我们讲完了,但是想要更好的利用协程来处理高并发的任务,并不是一件容易的事情。我们经常了解多线程的概念,那么在实际场景中,到底有多少个多线程呢?多少合适?这是一个值得思考的问题。
为了更好地利用线程,池化的思想慢慢演变。是的,它是一个线程池。
当谈到协程时,这种想法仍然很常见。我们来分享一下这篇文章的要点:Go语言中的优雅使用协程池。
为什么会出现池化理念?池化思想的演变主要源于高效资源利用和系统性能优化的需求。它是一种预先创建并管理资源(如线程、数据库连接等)以实现资源高效利用、降低开销的策略。
降低资源创建和销毁的成本:在多线程编程中,线程的创建和销毁都是资源密集型操作,会消耗大量的时间和系统资源。通过线程池,可以提前创建和维护一组可复用的线程,避免频繁创建和销毁线程的开销。
提高系统响应速度:池化技术通过预先分配资源,无需等待资源创建,使系统在面对任务时能够快速响应。这提高了系统响应能力和吞吐量。
Go优雅的协程池:如果蚂蚁想在项目中更高效的使用goruntine,协程池似乎是必备工具,因为Go语言很简单,自己写一个协程池并不难,但是它作为一个还没有达到顶级水平的Gopher,学习别人优秀的代码是一个需要经历的过程。众所周知,程序员的日常生活只有三件事:学习!学习!还是要多多学习啊!废话不多说,这次分享的蚂蚁是一个非常好用的Go协程池包。
其Github地址:github.com/panjf/ants。使用前需要下载依赖: 代码语言:shell copy go get -u github.com/panjf/ants/v2 然后我们只实现一个功能:counting,即从1到0添加一个名为num的变量。
假设每个操作耗时1ms (1)单线程单线程场景:code 语言:go copy var num int32func addNum(i int32) {atomic.AddInt32(&num, i)time.Sleep(time.Millisecond)fmt.Println ("now num = ", num)}func TestNum(t *testing.T ) {runTimes := 0for i := 0;我 < 运行时间; i++ {addNum(1)}fmt.Printf("result num = %d \n ", num)}看执行时间,居然花了几秒:代码语言:shell copy result num = 0 --- PASS: TestNum (.18s) PASS (2) 初次使用Ants 现在我们启用Ants: 代码语言: go copy func TestAnt(t *testing.T) {defer ants.Release ()var wgsync.WaitGroupsyncCalculateSum := func() {addNum (1)wg.Done()}运行次数:= 0for i := 0; i < 运行时间; i++ {wg.Add(1)_ = ants.Submit(syncCalculateSum) //需要执行的方法}wg.Wait()fmt.Printf("running goroutines: %d\n", ants.Running())fmt.Printf("num = %d \n ", num)} 执行结果仅0.31秒!代码语言:shell复制运行goroutines: num = 0 - -- PASS: TestAnt (0.31s)PASS 但它的缺点是同时使用两个协程,同时使用大量的资源无非是对服务器的一个很大的考验,所以不太好。优雅的。
(3)如果想用Ant优雅地控制协程数量,我们可以使用协程数量有限的协程池。比如设定一个小目标,先使用: 代码语言: go copy func TestAntPool(t *testing.T) {defer ants.Release()var wgsync.WaitGroupf := func() {addNum(1)wg. Done()}runTimes := 0pool, _ := ants.NewPool()for i := 0;我 < 运行时间 ; i++ {wg.Add(1)_ = pool.Submit(f)}wg.Wait()fmt.Printf("正在运行的 goroutine: %d\n", pool.Running())fmt.Printf("num = % d \n ", num)}执行结果仅1.21秒。
虽然时间长了一点,但是优雅多了!代码语言:shell copy running goroutines: num = 0 --- PASS: TestAntPoolAndWithPanicHandler (1.21s) PASS (3) 使用Ants的另一种形式 另外,Ants还支持另一种形式来执行特定的函数: 代码语言:go copy func TestAntWithFunc (t *testing.T) {defer ants.Release()runTimes := 0var wgsync.WaitGroupp, _ := ants.NewPoolWithFunc(10, func(i 接口{}) {addNum(i .(int32))wg.完成()})defer p.Release()for i := 0;我 < 运行时间; i++ {wg.Add(1)_ = p.Invoke(int32(1)) //这个Local参数可以通过结构体传递}wg.Wait()fmt.Printf("running goroutines: %d\n", p .Running())fmt.Printf("完成所有任务,结果为%d\n", num )}此外,Ants还提供了NewMultiPool类来初始化多个协程池。您可以根据预定义的策略从多个协程池中获取工作代码:轮询或最少使用策略。
语言: go copy func TestMultiPool(t *testing.T) { defer ants.Release()runTimes := 0var wgsync.WaitGroupf := func() {addNum(1)wg.Done()}//10表示初始化10协程池,-1位置参数表示协程池容量,当值为-1时表示无限容量 mp, _ := ants.NewMultiPool(10, -1, ants.RoundRobin)defer func() {_ = mp .ReleaseTimeout(5 * time.Second)}()for i := 0;我 < 运行时间; i++ {wg.Add(1)_ = mp.Submit(f)}wg.Wait()fmt.Printf("正在运行的协程: %d\n", mp.Running() )fmt.Printf("完成所有任务, result is %d\n", num)}好了,关于Ants的使用就分享到这里吧~协程池的应用场景 协程池并不是任何时候任何业务都会用到它,它是适用于所有的,而且它也有一些典型应用场景,如: 高并发处理:当系统需要处理大量并发请求时,使用协程池可以有效管理并发任务,避免资源耗尽,提高系统稳定性和性能。资源密集型任务:对于执行时间较长或占用系统资源较多的任务,使用协程池可以控制并发数,避免过多使用系统资源。
任务调度和负载均衡:在需要任务调度和负载均衡的场景中,协程池可以提供有效的任务排队和调度机制,保证任务能够按照预定的策略执行。综上所述,Go语言的协程池在并发编程中发挥着重要作用。
通过合理使用协程池,可以优化系统资源的使用,提高并发性能和吞吐量,简化并发编程的复杂度。
版权声明:本文内容由互联网用户自发贡献,本站不拥有所有权,不承担相关法律责任。如果发现本站有涉嫌抄袭的内容,欢迎发送邮件 举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。
标签:
相关文章
06-17
06-17
06-17
06-18
06-18
06-06
06-18
06-18
最新文章
【玩转GPU】ControlNet初学者生存指南
【实战】获取小程序中用户的城市信息(附源码)
包雪雪简单介绍Vue.js:开学
Go进阶:使用Gin框架简单实现服务端渲染
线程池介绍及实际案例分享
JMeter 注释 18 - JMeter 常用配置组件介绍
基于Sentry的大数据权限解决方案
【云+社区年度征文集】GPE监控介绍及使用