Go语言凭借 goroutine 与 channel 的原生并发支持,成为后端高并发服务的常用选择。goroutine 开销小,可以轻松创建数万个并发协程;channel 提供协程间通信的同步机制,避免共享内存带来的锁竞争。本文用实际代码讲解 Go 并发模型的正确用法、常见并发模式与性能排查方法。
Goroutine 的启动与退出控制
用 go 关键字启动 goroutine,但要注意防止泄漏:协程启动后必须有人负责结束它。常用 context 传递取消信号,让协程在任务完成后或收到取消时及时退出。
package main
import (
"context"
"fmt"
"time"
)
func worker(ctx context.Context, id int) {
for {
select {
case <-ctx.Done():
fmt.Printf("worker %d 退出\n", id)
return
default:
fmt.Printf("worker %d 处理中\n", id)
time.Sleep(500 * time.Millisecond)
}
}
}
func main() {
ctx, cancel := context.WithCancel(context.Background())
go worker(ctx, 1)
time.Sleep(2 * time.Second)
cancel() // 触发所有协程退出
time.Sleep(1 * time.Second)
}
长时间运行的协程要监听 context 取消信号,服务关闭时优雅退出,避免资源泄漏。
Channel 的两种典型用法
channel 用于协程间传递数据。无缓冲 channel 同步收发,有缓冲 channel 解耦生产与消费速度。关闭 channel 用 close,向已关闭 channel 发送数据会 panic,接收方用 ok 判断是否已关闭。
func producer(ch chan<- int) {
for i := 0; i < 10; i++ {
ch <- i
}
close(ch)
}
func consumer(ch <-chan int) {
for v := range ch {
fmt.Println("收到:", v)
}
}
func main() {
ch := make(chan int, 5)
go producer(ch)
consumer(ch)
}
只用 range 消费已关闭的 channel,生产者负责 close,不要在消费者侧关闭。
并发编程常用模式
生产环境常用三种并发模式:WaitGroup 等待批量任务、errgroup 收集错误、worker pool 控制并发数。
import "golang.org/x/sync/errgroup"
func main() {
g, ctx := errgroup.WithContext(context.Background())
for i := 0; i < 10; i++ {
i := i
g.Go(func() error {
select {
case <-ctx.Done():
return ctx.Err()
default:
// 模拟业务处理
return process(i)
}
})
}
if err := g.Wait(); err != nil {
fmt.Println("存在失败任务:", err)
}
}
errgroup 任一任务返回错误会取消 context,其余任务收到取消信号退出。并发请求外部服务时用 errgroup + semaphore 组合控制并发上限。
并发安全问题与排查
共享变量必须加锁或用原子操作。注意读多写少场景优先用 sync.RWMutex。排查并发 bug 用 go race 检测器,在测试与 CI 中开启。
go run -race main.go
go test -race ./...
生产环境收到数据竞争报告要立即处理,race 导致的行为是不可预期的。把 -race 固定进测试命令,问题在 CI 阶段就暴露。
高并发服务治理要点
并发只是手段,服务治理才是目标。三件事必做:超时控制(每个外部调用都设超时)、限流(令牌桶或漏桶算法)、熔断(失败率超阈值快速失败)。监控上采集 goroutine 数量、P99 延迟、错误率,指标异常时优先看慢外部依赖与锁竞争。
原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/go-yu-yan-gao-bing-fa-fu-wu-shi-zhan-goroutine-yu-channel/