您好,登錄后才能下訂單哦!
今天小編給大家分享一下go高并發時append出錯怎么解決的相關知識點,內容詳細,邏輯清晰,相信大部分人都還太了解這方面的知識,所以分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后有所收獲,下面我們一起來了解一下吧。
背景
在實現圖片轉碼的需求時,需要支持最大 500 個圖片下載后轉換格式;
如果是一個一個下載后轉碼,耗時太長,需要使用 goroutine 實現 500 個圖片并發下載后,并發轉碼;
但自測過程中發現,會偶現下載后只轉換了 499 個圖片或更少的情況(全部下載、轉碼成功的條件下);
然后就開始了打印日志找 bug 的過程。
排查問題
因為并發時使用到了 sync 等待全部協程結束,起初以為是 sync 異步等待出了問題;
打印日志發現,正常執行了 500 次下載,執行完成下載之后,繼續執行的轉碼操作,排除 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() // 執行結束,協程減 1
fullname := config.TranscodeDownloadPath + "/" + uuid.NewV4().String() // 需要確保文件名的唯一性 (防止不同用戶同一時間操作了同一文件,導致轉碼失敗)
err := utils.DownloadCeph(value.(string), fullname) // 下載文件
// 下載文件狀態記錄
if err != nil {
*failedFiles = append(*failedFiles, fullname)
} else {
*successFiles = append(*successFiles, fullname)
}
}(value)
}
}
// 前端傳入的圖片 url
strUrlList := req["strUrlList"]
// 初始化變量
nWait := sync.WaitGroup{} // 多協程異步等待
var successFiles []string // 下載成功文件
var failedFiles []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-29 21:28:51.996 ERROR services/tools.go:149 開始下載!長度:500
2022-10-29 21:28:52.486 ERROR services/tools.go:153 下載結束!長度:499
2022-10-29 21:28:52.486 ERROR services/tools.go:155 開始轉碼!
打印更詳細的日志,對 for range 循環內的邏輯進行排查;
在單個 for 循環結束時增加日志:
log.Error("下載協程結束: ", len(*successFiles))
發現一處特殊的日志:
2022-10-29 21:40:38.407 ERROR services/tools.go:35 下載協程結束: 63
2022-10-29 21:40:38.407 ERROR services/tools.go:35 下載協程結束: 64
2022-10-29 21:40:38.407 ERROR services/tools.go:35 下載協程結束: 65
2022-10-29 21:40:38.407 ERROR services/tools.go:35 下載協程結束: 65
2022-10-29 21:40:38.408 ERROR services/tools.go:35 下載協程結束: 66
2022-10-29 21:40:38.408 ERROR services/tools.go:35 下載協程結束: 67
兩次長度都是 65,切片長度沒有發生變化,同一時間點執行兩次切片 append 方法,會偶現一次失效,問題原因找到;
解決問題
使用切片索引進行賦值,不再使用 append ;
修復代碼如下:
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() // 執行結束,協程減 1
fullname := config.TranscodeDownloadPath + "/" + uuid.NewV4().String() // 需要確保文件名的唯一性 (防止不同用戶同一時間操作了同一文件,導致轉碼失敗)
err := utils.DownloadCeph(value.(string), fullname) // 下載文件
// 下載文件狀態記錄
if err != 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出錯怎么解決”這篇文章的所有內容,感謝各位的閱讀!相信大家閱讀完這篇文章都有很大的收獲,小編每天都會為大家更新不同的知識,如果還想學習更多的知識,請關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。