关于Go协程分批次执行并复用前序结果的技术咨询
分批次启动Goroutine:先完成父级数据获取,再处理子级数据
嘿,这事儿其实很好搞定!咱们核心就是要保证第一批goroutine全部执行完毕、收集好所有父级数据后,再启动第二批goroutine去处理子级数据。下面给你具体的实现思路和代码示例:
核心思路
- 用
sync.WaitGroup等待第一批所有goroutine完成,收集它们返回的父级数据结果 - 基于第一批的结果,启动第二批goroutine,同样用
WaitGroup等待所有子级任务完成 - 可以结合切片或通道传递第一批的结果,这里先给你最直观的切片实现,再补充通道优化方案
代码示例
假设你的Reddit订阅源包提供了GetParentFeed()方法获取单条父级数据,GetChildData(parentID string)方法根据父级ID获取子级数据:
package main import ( "fmt" "sync" // 导入你的Reddit订阅源包 // "your/reddit/package" ) // 定义父级数据结构,根据实际返回调整 type ParentFeed struct { ID string Title string // 其他字段... } // 定义子级数据结构 type ChildData struct { ParentID string Content string // 其他字段... } func main() { // ---------------------- 第一批:获取父级数据 ---------------------- var parentWG sync.WaitGroup parentFeeds := make([]ParentFeed, 0) parentMu := sync.Mutex{} // 保护切片写入的互斥锁 // 假设要获取5个父级订阅源(数量按需调整) parentCount := 5 parentWG.Add(parentCount) // 启动第一批goroutine for i := 0; i < parentCount; i++ { go func(idx int) { defer parentWG.Done() // 调用Reddit包获取父级数据 feed, err := reddit.GetParentFeed() if err != nil { fmt.Printf("获取第%d个父级数据失败:%v\n", idx, err) return } // 加锁写入结果切片,避免数据竞争 parentMu.Lock() parentFeeds = append(parentFeeds, feed) parentMu.Unlock() }(i) } // 阻塞等待第一批所有goroutine完成 parentWG.Wait() fmt.Printf("第一批任务完成,共获取%d条父级数据\n", len(parentFeeds)) // ---------------------- 第二批:处理子级数据 ---------------------- var childWG sync.WaitGroup childResults := make([]ChildData, 0) childMu := sync.Mutex{} // 保护子级结果切片的互斥锁 childWG.Add(len(parentFeeds)) // 遍历父级结果,启动第二批goroutine for _, feed := range parentFeeds { go func(parent ParentFeed) { defer childWG.Done() // 根据父级ID获取子级数据 childData, err := reddit.GetChildData(parent.ID) if err != nil { fmt.Printf("获取父级[%s]的子级数据失败:%v\n", parent.Title, err) return } // 加锁写入子级结果切片 childMu.Lock() childResults = append(childResults, childData) childMu.Unlock() }(feed) } // 等待第二批所有goroutine完成 childWG.Wait() fmt.Printf("第二批任务完成,共获取%d条子级数据\n", len(childResults)) // 后续可以继续处理子级数据... }
优化方案:用通道替代互斥锁
如果不想用互斥锁,也可以用通道来安全传递第一批结果,代码会更简洁:
// 第一批改用通道收集结果 parentChan := make(chan ParentFeed, parentCount) // 启动goroutine时将结果发送到通道 go func(idx int) { defer parentWG.Done() feed, err := reddit.GetParentFeed() if err == nil { parentChan <- feed } }(i) // 等待第一批完成后关闭通道 parentWG.Wait() close(parentChan) // 从通道读取所有父级结果 var parentFeeds []ParentFeed for feed := range parentChan { parentFeeds = append(parentFeeds, feed) }
这样就不用手动加锁,利用Go通道的特性天然避免了数据竞争。
内容的提问来源于stack exchange,提问作者Alex Kirwan
相关产品推荐
相关产品推荐

