DEV Community 中文 关注 Go 中的有界并行:限制 Goroutine 数量的信号量模式 Go 语言的并发模型虽然强大,能够利用 goroutine 实现高效并发,但在同时启动大量 goroutine 时,可能导致下游服务过载或触及系统限制。对于诸如获取大量 URL 这类 I/O 密集型任务,这一问题尤为突出。若为每个 URL 启动一个 goroutine,这种常见的初始做法可能会耗尽文件描述符等资源,或触发 API 速率限制。其核心问题在于无界扇出(unbounded fan-out),即并发操作的数量缺乏控制。为解决这一问题,必须对并发进行限制。一种有效的方法是使用带缓冲的通道作为计数信号量,其中向通道发送操作可获取一个槽位,接收操作则释放该槽位。对于更复杂的场景,可使用 x/sync/semaphore 包,它支持加权并发限制和基于上下文的获取机制。对于涉及错误处理且需要在失败时停止其他操作的场景,推荐使用带有 SetLimit 方法的 x/sync/errgroup 包。errgroup 还提供派生上下文,当发生错误时该上下文会被取消,从而允许其他 goroutine 优雅退出。多个 goroutine 向不同切片索引写入是安全的,但共享可变状态则需要同步。选择合适的并发限制工具,取决于工作负载是否均匀、是否需要错误传播以及是否需要加权限制。最优的并发限制由链路中最薄弱的资源决定,通常是外部 API 的容忍度,而非 CPU 核心数。有界的并行性确保 goroutine 数量随资源规模扩展,而非随输入规模增长,从而防止服务崩溃。 Bounded Parallelism in Go: The Semaphore Pattern That Caps Goroutines dev.to
x/sync/semaphore包,它支持加权并发限制和基于上下文的获取机制。对于涉及错误处理且需要在失败时停止其他操作的场景,推荐使用带有SetLimit方法的x/sync/errgroup包。errgroup还提供派生上下文,当发生错误时该上下文会被取消,从而允许其他 goroutine 优雅退出。多个 goroutine 向不同切片索引写入是安全的,但共享可变状态则需要同步。选择合适的并发限制工具,取决于工作负载是否均匀、是否需要错误传播以及是否需要加权限制。最优的并发限制由链路中最薄弱的资源决定,通常是外部 API 的容忍度,而非 CPU 核心数。有界的并行性确保 goroutine 数量随资源规模扩展,而非随输入规模增长,从而防止服务崩溃。