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

新手玩Go协程的一些小坑

大家好,我是一根甜苦瓜,今天来分享新手玩Go协程的一些小坑

1. 问题代码

我们先来看下面的三段代码,看能不能看出问题,如果能看出问题并找到解决方案,那么恭喜你,入行了,后面的文章不用读了。代码如下

// 代码块一
func handleV1(items []Request){for _,item := range items{go procesItem(item)}
}// 代码块2
func handleV2(items chan Request){for item := range items{go procesItem(item)}
}
// 代码块3
func handleV3(items []Request) error {for _,item := range items{go func(i item){if err := processItem(i);err != nil{}}(item)}return nil
}

2 逐个分析

下面来逐个分析上面代码的问题在哪,以及解决方案

2.1 协程泄漏

func handleV1(items []Request){for _,item := range items{go procesItem(item)}// 上面的协程还没全部处理完,函数已经返回了
}

我们先来分析代码块一,他存在明显的协程泄漏问题!也就是说handleV1函数返回时,没有等待所有procesItem完成,而这些groutine还在运行。他的弊端如下:

  1. 资源泄露,和不可控的退出,这是最大的问题。
  2. 在服务关闭或者任务取消时,这些groutine无法优雅停止
  3. 可能会导致内存泄漏

解决方案:最简单的就是用sync.Waitgroup + context

func handleV1(ctx context.Context,items []Request){var wg sync.WaitGroupfor _,item := range items{wg.Add(1)go func(r Request) {defer wg.Done()select {case <-ctx.Done():return // 上下文取消时优雅退出default:procesItem(r)}}(req)}wg.Wait() // 等待所有任务完成
}

上面的代码主要用了sync.WaitGroup方法,具体来说就是每启动一个groutine就执行一次wg.Add(1)方法,最后在返回之前调用 wg.Wait()方法,他会等待所有groutine执行结束。同时使用context来监听上下文的级联信号,这是一个很常见的小技巧。

2.2 无限制创建协程

// 代码块2
func handleV2(items chan Request){for item := range items{go procesItem(item)}
}

我们再来看看代码块二,他最大的问题是协程数量不可控,当request来得太快,会创建不可估量的groutine。虽然goroutine是很轻量的,但是如果不限制数量还是会出现CPU飙升,内存爆炸,GC压力过等问题。所以一定要对协程数量进行限制。

解决方案:常用的方法是用协程池来解决。对于协程池,业界也有很好的开源工具比如:https://github.com/panjf2000/ants,这里我们自己手写一个简单的协程池。

func handleHighTraffic(items chan Request) {const workerCount = 10var wg sync.WaitGroup// 启动固定数量的 workerfor i := 0; i < workerCount; i++ {wg.Add(1)go func() {defer wg.Done()for req := range items {procesItem(req)}}()}wg.Wait()
}

当然,实际生产环境还是建议搭建使用功能更强大的协程池工具,比如https://github.com/panjf2000/ants

2.3 错误处理困难(异步执行错误无法返回)

func handleV3(items []Request) error {for _,item := range items{go func(i item){if err := processItem(i);err != nil{}}(item)}return nil
}

上面代码的问题也是初学者懊恼的一个问题,如果多个groutine执行期间,某一些协程出现了error,那么主逻辑应该如何捕获这些error?肯定不能像上面那样直接return nil,那么应该如何汇总错误,特别是在批量任务的时候,如何判断成功或失败呢?

解决方案:errgroup(Go官方推荐的库)
Go 提供了 golang.org/x/sync/errgroup,简洁优雅地解决此类问题。

import "golang.org/x/sync/errgroup"func batchProcess(ctx context.Context, items []Request) error {g, ctx := errgroup.WithContext(ctx)for _, item := range items {i := itemg.Go(func() error {return processItem(i)})}// 等待所有任务完成,并返回第一个错误return g.Wait()
}

errgroup是使用十分广泛的一个包,用来自动等待所有任务的同时,还能抓到第一个错误,我们无需手动维护sync.Waitgroup。

3. 总结

上面的手写的协程泄漏和协程池都是十分简陋的,大家可以去搜索一下一些优秀的开源项目,看看其源码,对于我们理解协程更有帮助。实际开发过程中我们应该时刻注意

  1. 任何groutine都必须有退出条件
  2. 所有groutine最好有数量控制
http://www.dtcms.com/a/478760.html

相关文章:

  • STM32的VDD和VSS,VDDA和VSSA,REF+与REF-。
  • 基于STM32的智能门禁系统(论文+源码)
  • 新乡网站建设价格怎么做网站模块
  • 中小企业建设网站补贴企业网站推广的重要性
  • 信息比率诊断工具开发量化评估ETF网格择时能力有效性
  • 栏位索引超过许可范围:4,栏位数:3。; nested exception is org.postgresql.util.PSQLException
  • 厦门网站建设哪家强徐州58同城网
  • 如何进行新产品的推广网站seo技术
  • Dioxus状态管理
  • 微调高级推理大模型(COT)的综合指南:从理论到实践
  • 做美食分享网站源码wordpress网址一大串
  • 深圳做网站的人百度竞价点击软件奔奔
  • uniapp学习【整体实践】
  • Rabbitmq如何避免消息丢失
  • 建设一个朋友的网站工商局注册公司网站
  • wap网站建设免费关于网站建设费用的报告
  • asp网站开发实训报告亚马逊开店需要什么条件
  • cms管理手机网站制作网站的页面设计怎么做
  • 湖北工程公司建设公司网站腾讯云服务器免费体验
  • 面试问题—你接受加班吗?
  • 使用Asp.Net WebApi(.net 8)托管Unity WebGL
  • 用凡科做网站需要花钱吗localhostwordpress打不开
  • 15 【C++11 新特性】统一的列表初始化和变量类型推导
  • 合肥制作网站单位有哪些免费网站
  • 【密码学实战】openHiTLS client命令行:国密TLCP/DTLCP客户端工具
  • 鸿蒙:将项目的rawfile目录下全部文件拷贝到app沙箱目录(第二种方案)
  • PWM输出频率计
  • RuntimeBroker.exe应用程序错误?3种专业修复方法
  • PHP网站开发有哪些框架互联网推广品牌
  • [Power BI] VALUES函数