go高并发时append出错怎么解决


今天小编给大家分享一下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怎么使用”吧!到此免费云主机域名,相信大家对“SSH框架Hibernate怎么使用”有…

免责声明:本站发布的图片视频文字,以转载和分享为主,文章观点不代表本站立场,本站不承担相关法律责任;如果涉及侵权请联系邮箱:360163164@qq.com举报,并提供相关证据,经查实将立刻删除涉嫌侵权内容。

(0)
打赏 微信扫一扫 微信扫一扫
上一篇 02/18 11:29
下一篇 02/18 11:29

相关推荐