[Go]-控制 Goroutine 数量的代码实现过程

在这里插入图片描述


Go在处理多任务的过程,很容易有通道并发操作,这里讲解一个常见的并发控制流程,通过控制Goroutine的数量,来防止过多的 Goroutine 会导致调度开销过高,甚至耗尽内存或文件描述符。

方案:使用 Goroutine 池: 限制 Goroutine 的最大并发数量

话不多说,先看代码

package main

import (
    "fmt"
    "sync"
)

func worker(task int, wg *sync.WaitGroup) {
    defer wg.Done()
    fmt.Printf("Processing task: %d\n", task)
}

func main() {
    tasks := make([]int, 1000) // 模拟 1000 个任务
    for i := range tasks {
        tasks[i] = i
    }

    const maxWorkers = 10
    taskCh := make(chan int, len(tasks))
    var wg sync.WaitGroup

    // 启动固定数量的 Goroutine
    for i := 0; i < maxWorkers; i++ {
        go func() {
            for task := range taskCh {
                worker(task, &wg)
            }
        }()
    }

    // 分发任务
    for _, task := range tasks {
        wg.Add(1)
        taskCh <- task
    }
    close(taskCh)

    wg.Wait()
    fmt.Println("All tasks completed")
}

1. 总体逻辑

代码实现了一个固定 Goroutine 池来并发处理任务的模型。

  • 目标:限制并发 Goroutine 的数量,避免过多 Goroutine 导致调度开销过大。
  • 逻辑:
    • 将所有任务放入 tasks 列表。
    • 创建一个缓冲 Channel (taskCh) 来存储任务。
    • 创建一个 Goroutine 池,每个 Goroutine 从 taskCh 中取任务并处理。
    • 使用 sync.WaitGroup 等待所有任务完成后退出程序。

2. 关键逻辑解析

2.1 模拟任务列表
tasks := make([]int, 1000) // 模拟 1000 个任务
for i := range tasks {
    tasks[i] = i
}
  • 功能:创建一个包含 1000 个任务的列表(任务用整数表示)。
  • 目的:提供足够的任务,用于模拟大规模任务处理。

2.2 控制最大并发数
const maxWorkers = 10
taskCh := make(chan int, len(tasks))
  • 功能:
    • maxWorkers = 10:设置最多允许 10 个 Goroutine 并发处理任务。
    • taskCh:使用缓冲 Channel 存储待处理的任务。
      • 缓冲大小设置为 len(tasks),确保任务队列不会因缓冲区满而阻塞。

2.3 启动固定数量的 Goroutine
for i := 0; i < maxWorkers; i++ {
    go func() {
        for task := range taskCh {
            worker(task, &wg)
        }
    }()
}
  • 逻辑:

    • 启动 maxWorkers 个 Goroutine,每个 Goroutine 持续从 taskCh 中取任务处理。
    • 每个任务由 worker 函数处理。
  • 关键点:

    • for task := range taskCh:
      
      • Goroutine 会阻塞在这里,直到从 taskCh 取到任务。
      • taskCh 被关闭时,range 自动退出,Goroutine 结束。

2.4 分发任务
for _, task := range tasks {
    wg.Add(1)
    taskCh <- task
}
close(taskCh)
  • 逻辑:
    • 遍历 tasks,将每个任务放入 taskCh
    • 使用 wg.Add(1) 增加 sync.WaitGroup 的计数,表示有一个任务待完成。
    • 分发完所有任务后,关闭 taskCh,通知所有 Goroutine 没有新任务了。

2.5 等待任务完成
wg.Wait()
fmt.Println("All tasks completed")
  • 逻辑:
    • wg.Wait() 阻塞主 Goroutine,直到所有任务完成(即 wg.Done() 被调用完)。
    • 所有任务完成后打印 "All tasks completed",程序退出。

3. 核心组件分析

3.1 sync.WaitGroup 的作用
  • 功能:
    • 用于等待所有任务完成。
  • 使用方法:
    • 每分发一个任务时调用 wg.Add(1)
    • 每完成一个任务时调用 wg.Done()
    • 调用 wg.Wait() 阻塞,直到计数归零。

3.2 缓冲 Channel 的作用
  • 功能:
    • 存储任务,控制任务流速,避免 Goroutine 因取不到任务而频繁阻塞。
  • 缓冲大小:
    • 设置为 len(tasks),确保所有任务可以全部放入 Channel 而不阻塞。

3.3 固定 Goroutine 池
  • 目的:
    • 限制并发数,避免创建过多 Goroutine 导致性能问题。
  • 实现:
    • 使用一个固定数量的 Goroutine 取任务并处理。

4. 优点与适用场景

优点
  1. 限制并发数:通过固定 Goroutine 池控制最大并发数。
  2. 高效资源利用:避免因过多 Goroutine 导致的调度开销。
  3. 简单灵活:适用于需要批量处理任务的场景。
适用场景
  • 大量任务需要并发处理,但任务处理时间相对均衡。
  • 高并发场景下需要控制 Goroutine 数量。

以上通过 Goroutine 池的方式,实现了高效的并发任务调度,是 Go 并发编程中的经典模式之一。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ifanatic

觉得对您有用,可以友情打赏

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值