如何用Golang实现批量并发任务执行

来源:站长联盟作者:老毕头衔:草根站长
导读:本期聚焦于小伙伴创作的《如何用Golang实现批量并发任务执行》,敬请观看详情,探索知识的价值。以下视频、文章将为您系统阐述其核心内容与价值。如果您觉得《如何用Golang实现批量并发任务执行》有用,将其分享出去将是对创作者最好的鼓励。

在Golang开发场景中,经常需要处理大量同类型的重复任务,比如批量处理文件、批量调用第三方接口、批量计算数据等。如果采用串行执行的方式,会极大浪费CPU资源,延长整体执行时间,因此通过并发方式批量执行任务是非常必要的。

如何用Golang实现批量并发任务执行

核心实现思路

Golang实现批量并发任务的核心是利用goroutine创建并发执行单元,结合channel进行任务分发和结果收集,同时需要通过sync包的相关组件控制并发流程,避免协程泄漏或者资源过度占用。

基础并发执行方案

最基础的批量并发实现方式是遍历任务列表,为每个任务启动一个goroutine,然后通过sync.WaitGroup等待所有任务完成。这种方式适合任务数量不多且单个任务资源消耗较小的场景。

package main

import (
	"fmt"
	"sync"
	"time"
)

// 定义任务函数,模拟单个任务的执行
func singleTask(taskID int, wg *sync.WaitGroup) {
	defer wg.Done()
	// 模拟任务执行耗时
	time.Sleep(time.Millisecond * 500)
	fmt.Printf("任务 %d 执行完成n", taskID)
}

func main() {
	// 模拟批量任务列表,共10个任务
	taskCount := 10
	var wg sync.WaitGroup
	// 遍历任务列表,为每个任务启动goroutine
	for i := 0; i < taskCount; i++ {
		wg.Add(1)
		go singleTask(i, &wg)
	}
	// 等待所有任务完成
	wg.Wait()
	fmt.Println("所有批量任务执行完毕")
}

固定协程池方案

如果任务数量非常多,无限制启动goroutine会导致系统资源耗尽,此时可以采用固定协程池的策略,控制同时执行的协程数量。核心思路是创建固定数量的worker协程,通过任务通道分发任务,worker处理完一个任务后继续从通道获取下一个任务。

package main

import (
	"fmt"
	"sync"
	"time"
)

// 任务结构体,可根据实际需求扩展字段
type Task struct {
	ID int
}

// worker函数,处理任务的逻辑
func worker(workerID int, taskChan <-chan Task, wg *sync.WaitGroup) {
	defer wg.Done()
	for task := range taskChan {
		// 模拟任务处理耗时
		time.Sleep(time.Millisecond * 300)
		fmt.Printf("Worker %d 处理了任务 %dn", workerID, task.ID)
	}
}

func main() {
	// 定义任务总数和协程池大小
	taskCount := 20
	poolSize := 5
	taskChan := make(chan Task, taskCount)
	var wg sync.WaitGroup

	// 启动固定数量的worker协程
	for i := 0; i < poolSize; i++ {
		wg.Add(1)
		go worker(i, taskChan, &wg)
	}

	// 向任务通道发送所有任务
	for i := 0; i < taskCount; i++ {
		taskChan <- Task{ID: i}
	}
	close(taskChan)

	// 等待所有worker处理完任务
	wg.Wait()
	fmt.Println("所有任务通过协程池执行完毕")
}

常见批处理策略对比

不同的批处理策略适用不同的场景,下面通过表格对比两种常见方案的优缺点:

策略类型优点缺点适用场景
无限制goroutine并发实现简单,无需额外管理协程池任务数量多时容易造成资源耗尽,协程泄漏风险高任务数量少、单个任务资源消耗极低的场景
固定协程池并发可控并发数量,资源占用稳定,避免协程泄漏实现相对复杂,需要管理任务通道和worker生命周期任务数量多、单个任务有一定资源消耗的场景

注意事项

  • 使用sync.WaitGroup时,一定要保证Add的调用次数和Done的调用次数一致,否则会导致程序阻塞或者panic。
  • 任务通道使用完毕后要及时关闭,否则worker协程会一直阻塞在读取通道的操作上,造成协程泄漏。
  • 如果任务执行过程中可能出现错误,需要额外设计错误收集通道,避免错误被忽略,同时要注意错误通道的关闭时机。
  • 并发场景下如果多个任务需要操作共享资源,要通过sync.Mutex或者channel进行同步,避免数据竞争问题。

错误收集示例

在批量并发任务执行时,收集每个任务的错误信息也是常见需求,下面是在固定协程池基础上添加错误收集的实现:

package main

import (
	"fmt"
	"sync"
	"time"
)

type Task struct {
	ID int
}

// 模拟可能出错的任务执行
func executeTask(task Task) error {
	time.Sleep(time.Millisecond * 200)
	// 模拟ID为3的任务执行出错
	if task.ID == 3 {
		return fmt.Errorf("任务 %d 执行失败", task.ID)
	}
	return nil
}

func main() {
	taskCount := 10
	poolSize := 3
	taskChan := make(chan Task, taskCount)
	errChan := make(chan error, taskCount)
	var wg sync.WaitGroup

	// 启动worker,处理任务并收集错误
	for i := 0; i < poolSize; i++ {
		wg.Add(1)
		go func() {
			defer wg.Done()
			for task := range taskChan {
				if err := executeTask(task); err != nil {
					errChan <- err
				} else {
					fmt.Printf("任务 %d 执行成功n", task.ID)
				}
			}
		}()
	}

	// 发送任务
	for i := 0; i < taskCount; i++ {
		taskChan <- Task{ID: i}
	}
	close(taskChan)

	// 等待任务处理完成,然后关闭错误通道
	go func() {
		wg.Wait()
		close(errChan)
	}()

	// 收集所有错误
	var errList []error
	for err := range errChan {
		errList = append(errList, err)
	}

	fmt.Printf("执行完成,共 %d 个任务出错n", len(errList))
	for _, err := range errList {
		fmt.Println(err)
	}
}

Golang并发任务批量执行任务批处理goroutine修改时间:2026-07-17 18:36:30

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。