每日智识
柔彩主题三 · 更轻盈的阅读体验

Go并发执行任务队列:高效处理批量任务的实用方案

发布时间:2026-01-16 22:00:56 阅读:296 次

在日常开发中,经常会遇到需要处理大量任务的场景。比如公司内部的报表系统,每天要生成上百份数据报告;或者邮件推送服务,短时间内要发出数千封通知。如果一个个顺序执行,耗时会非常长,用户体验也不好。这时候,用 Go 的并发机制来实现一个任务队列,就显得特别实用。

为什么选择 Go?

Go 语言天生适合并发编程。它的 goroutine 轻量高效,启动成本低,加上 channel 的良好支持,让并发任务的协调变得简单直观。相比起其他语言中复杂的线程管理,Go 几乎可以用几行代码就搭出一个并发任务处理器。

一个简单的任务队列模型

设想你负责一个文件转码服务,用户上传视频后,系统需要将其转为多种格式。这类任务耗时较长,但彼此独立,非常适合并发处理。我们可以定义一个任务结构体,再通过 channel 来分发任务。

type Task struct {
    ID       int
    Filename string
}

func worker(tasks <-chan Task, results chan<- Task) {
    for task := range tasks {
        // 模拟转码耗时
        time.Sleep(2 * time.Second)
        fmt.Printf("已完成转码: %s\n", task.Filename)
        results <- task
    }
}

上面的 worker 函数会从 tasks 通道中不断读取任务,处理完成后把结果写入 results 通道。主函数可以启动多个 worker,形成一个工作池。

func main() {
    tasks := make(chan Task, 100)
    results := make(chan Task, 100)

    // 启动 3 个 worker
    for i := 0; i < 3; i++ {
        go worker(tasks, results)
    }

    // 添加 10 个任务
    for i := 0; i < 10; i++ {
        tasks <- Task{ID: i, Filename: fmt.Sprintf("video_%d.mp4", i)}
    }
    close(tasks)

    // 等待所有结果
    for i := 0; i < 10; i++ {
        <-results
    }
}

这样,原本需要 20 秒顺序处理的任务,现在大约 7 秒就能完成。效率提升明显,服务器资源也得到了更好利用。

实际应用中的优化点

在真实项目里,任务可能会失败,比如文件丢失或网络超时。可以在 worker 中加入重试逻辑,失败的任务重新放回队列,最多尝试三次。同时,用 context 控制整体超时,避免某些任务卡住太久。

还有一种情况是任务优先级不同。比如紧急通知要优先发送,普通日志可以稍后处理。这时可以引入多个 channel,按优先级分配 worker,或者用 select 非阻塞地监听高优先级通道。

这种模式不仅适用于后台服务,在 CLI 工具中也很有用。比如你写了个脚本要批量检测网站可用性,几百个 URL 用并发跑,几分钟就能出结果,而不是等上半小时。

技术本身没有高低,关键是怎么用。Go 的并发模型不复杂,但组合起来能解决很多实际问题。与其花时间搭建重型消息队列,不如先试试用原生语法快速实现一个轻量队列,效果可能超出预期。