
本文介绍如何使用 goroutine 和 sync.waitgroup 对 go 中的小型切片(如 ≤100 项)进行并发清洗操作,并分析其适用场景、实现细节与潜在风险。
本文介绍如何使用 goroutine 和 sync.waitgroup 对 go 中的小型切片(如 ≤100 项)进行并发清洗操作,并分析其适用场景、实现细节与潜在风险。
在 Go 中,若需对切片中每个元素执行相同计算密集或 I/O 可能延时的操作(例如 HTML 清洗、字符串标准化等),可借助 goroutine 实现并行化处理。但需注意:并行 ≠ 自动加速——尤其当数据量小(如仅百条)、单次操作轻量(如短字符串处理)时,goroutine 调度开销可能抵消甚至超过收益。
以下是以 cleanString 为例的并发改写方案:
import (
"sync"
"strings"
"github.com/microcosm-cc/bluemonday" // 示例 sanitize 包(原题中 sanitize.HTML)
)
func myfunction() {
results := make([]SomeCustomStruct, 0)
// ... results gets populated ...
var wg sync.WaitGroup
for index, value := range results {
wg.Add(1)
// 捕获 index 和 value.Body 的当前值,避免闭包变量共享问题
go func(i int, body string) {
defer wg.Done()
results[i].Body = cleanString(body)
}(index, value.Body)
}
wg.Wait()
// ... 后续逻辑 ...
}
func cleanString(in string) string {
s := bluemonday.UGCPolicy().Sanitize(in) // 替代 sanitize.HTML
s = strings.ReplaceAll(s, "\n", " ") // 使用 ReplaceAll(Go 1.12+ 推荐)
return strings.TrimSpace(s)
}
✅ 关键要点说明:
Colly 是一个用于 Go 语言的快速开源爬取和爬虫框架。它适用于从简单的页面提取到异步爬虫处理大量页面集合,支持请求回调和结构化解析。
- 必须显式传参:go func(i int, body string) 中将 index 和 value.Body 作为参数传入,防止因循环变量 index/value 在 goroutine 启动前被后续迭代覆盖而导致的数据竞争或错误赋值。
- 务必调用 wg.Wait():确保所有 goroutine 完成后再继续执行后续逻辑,否则可能读取未更新的 results。
- 避免过度并发:100 条以内数据通常无需限制 goroutine 数量;但若 cleanString 内部含网络请求或文件 I/O,建议结合 semaphore 或 worker pool 控制并发度(如 golang.org/x/sync/semaphore)。
⚠️ 注意事项:
- 若 SomeCustomStruct 字段为指针或包含非线程安全结构(如 map、slice 本身被多 goroutine 修改),需额外同步保护;本例仅写入 .Body 字符串字段,属安全场景。
- sync.WaitGroup 是最轻量的同步原语,适用于已知任务数的等待场景;不推荐用 time.Sleep 或通道盲目等待。
- 性能验证建议:对真实数据集使用 go test -bench 对比串行 vs 并行版本,避免“过早优化”。
总结:对小型切片启用 goroutine 并行是可行的,但应以实测为准。优先保证代码正确性与可维护性,再根据实际瓶颈决定是否引入并发——毕竟,清晰、安全的串行逻辑,永远比脆弱的并发更快。










