Go語言高并發(fā)時append方法偶現(xiàn)錯誤的解決方法
但自測過程中發(fā)現(xiàn),會偶現(xiàn)下載后只轉(zhuǎn)換了 499 個圖片或更少的情況(全部下載、轉(zhuǎn)碼成功的條件下);
然后就開始了打印日志找 bug 的過程。
排查問題
因為并發(fā)時使用到了 sync 等待全部協(xié)程結(jié)束,起初以為是 sync 異步等待出了問題;
打印日志發(fā)現(xiàn),正常執(zhí)行了 500 次下載,執(zhí)行完成下載之后,繼續(xù)執(zhí)行的轉(zhuǎn)碼操作,排除 sync 異步等待有問題;
代碼如下:
import?(
????"github.com/satori/go.uuid"
????"sync"
)
func?downloadFiles(nWait?*sync.WaitGroup,?urls?[]interface{},?successFiles?*[]string,?failedFiles?*[]string)?{
????//?遍歷?urls?進行下載
????for?_,?value?:=?range?urls?{
????????go?func(value?interface{})?{
????????????defer?nWait.Done()?????????????????????????????????????????????????????//?執(zhí)行結(jié)束,協(xié)程減?1
????????????fullname?:=?config.TranscodeDownloadPath?+?"/"?+?uuid.NewV4().String()?//?需要確保文件名的唯一性?(防止不同用戶同一時間操作了同一文件,導(dǎo)致轉(zhuǎn)碼失敗)
????????????err?:=?utils.DownloadCeph(value.(string),?fullname)????????????????????//?下載文件
????????????//?下載文件狀態(tài)記錄
????????????if?err?!=?nil?{
????????????????*failedFiles?=?append(*failedFiles,?fullname)
????????????}?else?{
????????????????*successFiles?=?append(*successFiles,?fullname)
????????????}
????????}(value)
????}
}
//?前端傳入的圖片?url
strUrlList?:=?req["strUrlList"]
//?初始化變量
nWait?:=?sync.WaitGroup{}??????????//?多協(xié)程異步等待
var?successFiles?[]string??//?下載成功文件
var?failedFiles?[]string???????????//?下載失敗文件
//?遍歷?strUrlList?進行下載
log.Error("開始下載!長度:",?len(strUrlList))
nWait.Add(len(strUrlList))?//?等待協(xié)程數(shù)
downloadFiles(&nWait,?strUrlList,?&successFiles,?&failedFiles)
nWait.Wait()?//?阻塞,等待完成
log.Error("下載結(jié)束!長度:",?len(successFiles))
//...
log.Error("下載轉(zhuǎn)碼!")
//...
日志如下:
2022-10-29 21:28:51.996 ERROR ? services/tools.go:149 ? 開始下載!長度:500
2022-10-29 21:28:52.486 ERROR ? services/tools.go:153 ? 下載結(jié)束!長度:499
2022-10-29 21:28:52.486 ERROR ? services/tools.go:155 ? 開始轉(zhuǎn)碼!
打印更詳細(xì)的日志,對 for range 循環(huán)內(nèi)的邏輯進行排查;
在單個 for 循環(huán)結(jié)束時增加日志:
log.Error("下載協(xié)程結(jié)束:?",?len(*successFiles))
發(fā)現(xiàn)一處特殊的日志:
2022-10-29 21:40:38.407 ERROR ? services/tools.go:35 ? ?下載協(xié)程結(jié)束: 63
2022-10-29 21:40:38.407 ERROR ? services/tools.go:35 ? ?下載協(xié)程結(jié)束: 64
2022-10-29 21:40:38.407 ERROR ? services/tools.go:35 ? ?下載協(xié)程結(jié)束: 65
2022-10-29 21:40:38.407 ERROR ? services/tools.go:35 ? ?下載協(xié)程結(jié)束: 65
2022-10-29 21:40:38.408 ERROR ? services/tools.go:35 ? ?下載協(xié)程結(jié)束: 66
2022-10-29 21:40:38.408 ERROR ? services/tools.go:35 ? ?下載協(xié)程結(jié)束: 67
兩次長度都是 65,切片長度沒有發(fā)生變化,同一時間點執(zhí)行兩次切片 append 方法,會偶現(xiàn)一次失效,問題原因找到;
解決問題
使用切片索引進行賦值,不再使用 append ;
修復(fù)代碼如下:
import?(
????"github.com/satori/go.uuid"
????"sync"
)
func?downloadFiles(nWait?*sync.WaitGroup,?urls?[]interface{},?successFiles?*[]string,?failedFiles?*[]string)?{
????//?遍歷?urls?進行下載
????for?index,?value?:=?range?urls?{
????????go?func(index?int,?value?interface{})?{
????????????defer?nWait.Done()?????????????????????????????????????????????????????//?執(zhí)行結(jié)束,協(xié)程減?1
????????????fullname?:=?config.TranscodeDownloadPath?+?"/"?+?uuid.NewV4().String()?//?需要確保文件名的唯一性?(防止不同用戶同一時間操作了同一文件,導(dǎo)致轉(zhuǎn)碼失敗)
????????????err?:=?utils.DownloadCeph(value.(string),?fullname)????????????????????//?下載文件
????????????//?下載文件狀態(tài)記錄
????????????if?err?!=?nil?{
????????????????(*failedFiles)[index]?=?fullname
????????????}?else?{
????????????????(*successFiles)[index]?=?fullname
????????????}
????????}(index,?value)
????}
}
//?前端傳入的圖片?url
strUrlList?:=?req["strUrlList"]
//?初始化變量
nWait?:=?sync.WaitGroup{}????????????????????????????????????????//?多協(xié)程異步等待
successFiles?:=?make([]string,?len(strUrlList),?len(strUrlList))?//?下載成功文件
failedFiles?:=?make([]string,?len(strUrlList),?len(strUrlList))??//?下載失敗文件
//?遍歷?strUrlList?進行下載
nWait.Add(len(strUrlList))?//?等待協(xié)程數(shù)
downloadFiles(&nWait,?strUrlList,?&successFiles,?&failedFiles)
nWait.Wait()?//?阻塞,等待完成
文章首發(fā):https://www.jb51.net/article/266329.htm