为何我的Go函数未等待goroutine执行完成?
问题分析
你的代码核心问题在于启动goroutine后立即读取未完成执行的结果:
- 循环中启动goroutine后,立刻访问
ed的Error和EncodedData,此时goroutine还未执行,这些值都是初始零值,导致results[i].IMG被提前赋值为空的base64前缀。 - 未关闭HTTP响应体,存在资源泄漏风险。
- 错误处理时机错误,goroutine还未执行就检查错误,完全起不到作用。
解决方案
1. 修复GetPainting函数(资源泄漏+健壮性)
import ( "encoding/base64" "errors" "io/ioutil" "net/http" "sync" ) type EncodedData string type EncodedImage struct { Data []byte EncodedData EncodedData Error error } func GetPainting(url string, EI *EncodedImage) { res, err := http.Get(url) if err != nil { EI.Error = errors.Wrapf(err, "unable to fetch from provided url %s", url) return } defer res.Body.Close() // 必须关闭响应体,避免连接泄漏 // 检查HTTP响应状态码 if res.StatusCode != http.StatusOK { EI.Error = errors.Errorf("unexpected status code: %d for url %s", res.StatusCode, url) return } body, err := ioutil.ReadAll(res.Body) if err != nil { EI.Error = errors.Wrapf(err, "failed to read response body from %s", url) return } encoded := base64.StdEncoding.EncodeToString(body) EI.Data, EI.EncodedData = body, EncodedData(encoded) }
2. 修正Gin处理器的并发逻辑
func Search(db *gorm.DB) gin.HandlerFunc { return func(c *gin.Context) { term := c.Param("term") var results []models.Searches // 先处理DB查询错误 if err := db.Table("searches").Where("to_tsvector(\"searches\".\"Title\" || '' || \"searches\".\"Artist_Name\") @@ plainto_tsquery(?)", term).Find(&results).Error; err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } var wg sync.WaitGroup // 创建与results长度匹配的切片,存储每个goroutine的结果 encodedImages := make([]EncodedImage, len(results)) // 可选:限制并发数,避免大量HTTP请求耗尽资源 sem := make(chan struct{}, 10) for i := range results { sem <- struct{}{} wg.Add(1) // 通过闭包传递索引,避免goroutine捕获循环变量的问题 go func(idx int) { defer func() { wg.Done() <-sem }() GetPainting(results[idx].IMG, &encodedImages[idx]) }(i) } // 等待所有goroutine执行完成 wg.Wait() // 统一处理错误+更新结果 for i := range results { if encodedImages[i].Error != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": encodedImages[i].Error.Error()}) return } results[i].IMG = fmt.Sprintf("data:image/jpeg;base64,%v", encodedImages[i].EncodedData) } c.JSON(http.StatusOK, results) } }
关键修改说明
- 资源泄漏修复:给
res.Body加上defer res.Body.Close(),确保HTTP连接被正确释放。 - 并发逻辑修正:
- 创建
encodedImages切片存储每个goroutine的结果,等待所有goroutine完成后再统一更新results。 - 使用带索引的闭包传递循环变量,避免goroutine捕获到复用的循环变量。
- 新增并发数限制(
sem通道),防止因结果数量过大导致的系统资源耗尽。
- 创建
- 错误处理优化:将错误检查移到所有goroutine执行完成后,避免单个请求错误导致其他goroutine资源泄漏,同时确保只有全部请求成功才返回最终结果。
内容的提问来源于stack exchange,提问作者Campbell Padgett
相关产品推荐
相关产品推荐

