今天小编给大家分享一下go高并发时append出错怎么解决的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。背景在实现图片转码的需求时,需要支持最大 500 个图片下载后转换格式;如果是一个一个下载后转码,耗时太长,需要使用 goroutine 实现 500 个图片并发下载后,并发转码;但自测过程中发现,会偶现下载后只转换了 499 个图片或更少的情况(全部下载、转码成功的条件下);然后就开始了打印日志找 bug 的过程。排查问题因为并发时使用到了 sync 等待全部协程结束,起初以为是 sync 异步等待出了问题;打印日志发现,正常执行了 500 次下载,执行完成下载之后,继续执行的转码操作,排除 sync 异步等待有问题;代码如下:
import( "github.com/satori/go.uuid" "sync" ) funcdownloadFiles(nWait*sync.WaitGroup,urls[]interface{},successFiles*[]string,failedFiles*[]string){ //遍历urls进行下载 for_,value:=rangeurls{ gofunc(valueinterface{}){ defernWait.Done()//执行结束,协程减1 fullname:=config.TranscodeDownloadPath+"/"+uuid.NewV4().String()//需要确保文件名的唯一性(防止不同用户同一时间操作了同一文件,导致转码失败) err:=utils.DownloadCeph(value.(string),fullname)//下载文件 //下载文件状态记录 iferr!=nil{ *failedFiles=append(*failedFiles,fullname) }else{ *successFiles=append(*successFiles,fullname) } }(value) } } //前端传入的图片url strUrlList:=req["strUrlList"] //初始化变量 nWait:=sync.WaitGroup{}//多协程异步等待 varsuccessFiles[]string//下载成功文件 varfailedFiles[]string//下载失败文件 //遍历strUrlList进行下载 log.Error("开始下载!长度:",len(strUrlList)) nWait.Add(len(strUrlList))//等待协程数 downloadFiles(&nWait,strUrlList,&successFiles,&failedFiles) nWait.Wait()//阻塞,等待完成 log.Error("下载结束!长度:",len(successFiles)) //... log.Error("下载转码!") //...
日志如下:
2022-10-2921:28:51.996ERRORservices/tools.go:149开始下载!长度:500 2022-10-2921:28:52.486ERRORservices/tools.go:153下载结束!长度:499 2022-10-2921:28:52.486ERRORservices/tools.go:155开始转码!
打印更详细的日志,对 for range 循环内的逻辑进行排查;在单个 for 循环结束时增加日志:
log.Error("下载协程结束:",len(*successFiles))
发现一处特殊的日志:
2022-10-2921:40:38.407ERRORservices/tools.go:35下载协程结束:63 2022-10-2921:40:38.407ERRORservices/tools.go:35下载协程结束:64 2022-10-2921:40:38.407ERRORservices/tools.go:35下载协程结束:65 2022-10-2921:40:38.407ERRORservices/tools.go:35下载协程结束:65 2022-10-2921:40:38.408ERRORservices/tools.go:35下载协程结束:66 2022-10-2921:40:38.408ERRORservices/tools.go:35下载协程结束:67
两次长度都是 65,切片长度没有发生变化,同一时间点执行两次切片 append 方法,会偶现一次失效,问题原因找到;解决问题使用切片索引进行赋值,不再使用 append ;修复代码如下:
import( "github.com/satori/go.uuid" "sync" ) funcdownloadFiles(nWait*sync.WaitGroup,urls[]interface{},successFiles*[]string,failedFiles*[]string){ //遍历urls进行下载 forindex,value:=rangeurls{ gofunc(indexint,valueinterface{}){ defernWait.Done(免费云主机域名)//执行结束,协程减1 fullname:=config.TranscodeDownloadPath+"/"+uuid.NewV4().String()//需要确保文件名的唯一性(防止不同用户同一时间操作了同一文件,导致转码失败) err:=utils.DownloadCeph(value.(string),fullname)//下载文件 //下载文件状态记录 iferr!=nil{ (*failedFiles)[index]=fullname }else{ (*successFiles)[index]=fullname } }(index,value) } } //前端传入的图片url strUrlList:=req["strUrlList"] //初始化变量 nWait:=sync.WaitGroup{}//多协程异步等待 successFiles:=make([]string,len(strUrlList),len(strUrlList))//下载成功文件 failedFiles:=make([]string,len(strUrlList),len(strUrlList))//下载失败文件 //遍历strUrlList进行下载 nWait.Add(len(strUrlList))//等待协程数 downloadFiles(&nWait,strUrlList,&successFiles,&failedFiles) nWait.Wait()//阻塞,等待完成
以上就是“go高并发时append出错怎么解决”这篇文章的所有内容,感谢各位的阅读!相信大家阅读完这篇文章都有很大的收获,小编每天都会为大家更新不同的知识,如果还想学习更多的知识,请关注百云主机行业资讯频道。
本篇内容主要讲解“SSH框架Hibernate怎么使用”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“SSH框架Hibernate怎么使用”吧!到此免费云主机域名,相信大家对“SSH框架Hibernate怎么使用”有…
免责声明:本站发布的图片视频文字,以转载和分享为主,文章观点不代表本站立场,本站不承担相关法律责任;如果涉及侵权请联系邮箱:360163164@qq.com举报,并提供相关证据,经查实将立刻删除涉嫌侵权内容。