Skip to content

Golang控制协程的并发量的几种方式

很多情况下,我们需要控制协程的并发量,比如客户端的设备承载限制,客户端业务层面的接口限制等等。我们有以下几种方式来控制。

1、分批请求控制

我们可以设置分批请求,来控制协程当前最多执行多少并发请求。示例:

go
func main() {
	total := 50
	batchSize := 10
	batchChuckReq(total, batchSize)
}

func batchChuckReq(total int, batchSize int) {
	for i := 0; i < total; i += batchSize {
		end := i + batchSize
		if end > total {
			end = total
		}

		var wg sync.WaitGroup
		for j := i; j < end; j++ {
			wg.Add(1)
			go func(j int) {
				defer wg.Done()
				exec(j)
			}(j)
		}
		wg.Wait() // 等这一批结束
		time.Sleep(time.Second * 1)
	}
}

func exec(j int) {
	fmt.Println(time.Now().Format(time.DateTime), j)
}

结果:

2025-10-09 10:11:04 2
2025-10-09 10:11:04 3
2025-10-09 10:11:04 5
2025-10-09 10:11:04 4
2025-10-09 10:11:04 6
2025-10-09 10:11:04 9
2025-10-09 10:11:04 7
2025-10-09 10:11:04 0
2025-10-09 10:11:04 8
2025-10-09 10:11:04 1
2025-10-09 10:11:05 13
2025-10-09 10:11:05 19
2025-10-09 10:11:05 14
2025-10-09 10:11:05 15
2025-10-09 10:11:05 16
2025-10-09 10:11:05 17
2025-10-09 10:11:05 18
2025-10-09 10:11:05 11
2025-10-09 10:11:05 10
2025-10-09 10:11:05 12
2025-10-09 10:11:06 20
2025-10-09 10:11:06 22
2025-10-09 10:11:06 21
2025-10-09 10:11:06 24
2025-10-09 10:11:06 25
2025-10-09 10:11:06 26
2025-10-09 10:11:06 27
2025-10-09 10:11:06 28
2025-10-09 10:11:06 29
2025-10-09 10:11:06 23
2025-10-09 10:11:07 39
2025-10-09 10:11:07 34
2025-10-09 10:11:07 30
2025-10-09 10:11:07 31
2025-10-09 10:11:07 32
2025-10-09 10:11:07 33
2025-10-09 10:11:07 36
2025-10-09 10:11:07 35
2025-10-09 10:11:07 37
2025-10-09 10:11:07 38
2025-10-09 10:11:08 49
2025-10-09 10:11:08 44
2025-10-09 10:11:08 40
2025-10-09 10:11:08 41
2025-10-09 10:11:08 42
2025-10-09 10:11:08 43
2025-10-09 10:11:08 46
2025-10-09 10:11:08 45
2025-10-09 10:11:08 47
2025-10-09 10:11:08 48

我们在每个批次执行之后, 延迟一秒再执行下一个批次。可以看到每个批次都协程执行完成了。

2、使用带缓冲的通道

使用带缓冲的通道,将通道的容量设置为每个批次的数量。循环所有要执行的数据,每次写入数据到通道中占位,在协程中释放。

go
func bufferChannelReq(total int, batchSize int) {
	ch := make(chan int, batchSize)
	var wg sync.WaitGroup
	for i := 0; i < total; i++ {
		wg.Add(1)
		// 占用一个位置
		fmt.Println("i=", i)
		ch <- i
		go func(i int) {
			defer wg.Done()
			time.Sleep(time.Second)
			// 每次调用释放一个位置
			fmt.Printf("i=%d, ch=%d\n", i, <-ch)
			exec(i)
		}(i)
	}
	wg.Wait()
}

3、速率控制

golang.org/x/time/rate 是 Go 官方扩展库(x 系列)中一个非常实用的**限流(Rate Limiting)**包, 它提供了一个高性能、线程安全的令牌桶算法(token bucket)实现。

go
import "golang.org/x/time/rate"

limiter := rate.NewLimiter(10, 20)
for i := 0; i < 100; i++ {
    limiter.Wait(context.Background()) // 等待许可
    go exec(i)
}