ホームページ >バックエンド開発 >Golang >Goの同時実行性が高い場合の追記エラー問題を詳しく解説!

Goの同時実行性が高い場合の追記エラー問題を詳しく解説!

藏色散人
藏色散人転載
2022-10-31 16:14:413069ブラウズ

この記事は、 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 サイトの他の関連記事を参照してください。

声明:
この記事はjuejin.imで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。