首頁  >  文章  >  後端開發  >  詳解go高併發時append出錯問題!

詳解go高併發時append出錯問題!

藏色散人
藏色散人轉載
2022-10-31 16:14:413029瀏覽

本文由golang教學專欄跟大家介紹關於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出錯問題!的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:juejin.im。如有侵權,請聯絡admin@php.cn刪除