この記事は、 golang チュートリアル というコラムで、go の同時実行性が高い場合に発生する append メソッドのエラーを紹介する記事として紹介されており、以下でその方法について詳しく説明します。困っている友達に!
背景
画像トランスコーディング要件を実装する場合、ダウンロードして形式を変換した後、最大 500 個の画像をサポートする必要があります。
画像をダウンロードする場合1 つずつトランスコードした後、時間がかかりすぎます。500 枚の画像の同時ダウンロードと同時トランスコードを実現するには、ゴルーチンを使用する必要があります。
ただし、セルフテスト中に、499 枚以上の画像しか取得できないことがわかりました。ほとんどのケースはありません (すべてのダウンロードとトランスコードが成功したという条件の下で);
その後、ログの出力とバグの発見のプロセスが始まります。
トラブルシューティング
同期はすべてのコルーチンが終了するのを待機するために同時実行中に使用されるため、最初は同期の非同期待機に問題があるのではないかと思いました。
ログの印刷 500 件のダウンロードが正常に実行されたことがわかりました。ダウンロードが完了した後、同期非同期待機の問題を解消するためにトランスコーディング操作が継続されました。
コードは次のとおりです:
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 で、スライスの長さは変更されていません。スライス追加メソッドを同時に 2 回実行すると、1 回失敗します。問題の原因が見つかりました;
問題を解決します
割り当てにスライス インデックスを使用し、追加は使用しません;
修復コードは次のとおりです。次のとおりです:
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の同時実行性が高い場合の追記エラー問題を詳しく解説!の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。