Golang的goroutine是语言层面支持的轻量级线程,由Go运行时管理,初始栈空间仅几KB,可轻松创建成千上万个实例,非常适合用来处理多任务并发场景。下面我们将从基础用法到多任务管理逐步展开讲解。

goroutine基础启动方式
启动goroutine非常简单,只需要在函数调用前加上go关键字即可,被调用的函数会在新的goroutine中异步执行,不会阻塞当前主线程的流程。
下面是一个最基础的goroutine示例:
package main
import (
"fmt"
"time"
)
// 定义要并发执行的函数
func printTask(taskName string) {
for i := 0; i < 3; i++ {
fmt.Printf("任务 %s 执行第 %d 次n", taskName, i)
time.Sleep(time.Millisecond * 500)
}
}
func main() {
// 启动第一个goroutine
go printTask("A")
// 启动第二个goroutine
go printTask("B")
// 主线程休眠等待goroutine执行完成,避免主程序提前退出
time.Sleep(time.Second * 2)
fmt.Println("主程序执行结束")
}
多任务并发执行场景实现
实际开发中常常需要同时执行多个同类任务,比如批量处理多个接口请求、并行处理多个文件等,这时候可以结合循环启动多个goroutine,配合sync.WaitGroup来管理所有任务的完成状态。
下面的示例演示了同时执行5个耗时任务,等待全部完成后才继续主流程的逻辑:
package main
import (
"fmt"
"sync"
"time"
)
// 单个任务的处理逻辑
func handleSingleTask(taskID int, wg *sync.WaitGroup) {
// 函数执行结束后通知WaitGroup任务完成
defer wg.Done()
fmt.Printf("任务 %d 开始执行n", taskID)
// 模拟任务耗时
time.Sleep(time.Second * 1)
fmt.Printf("任务 %d 执行完成n", taskID)
}
func main() {
// 定义WaitGroup用于等待所有goroutine完成
var wg sync.WaitGroup
// 要执行的任务总数
taskCount := 5
// 设置等待的任务数量
wg.Add(taskCount)
// 循环启动多个goroutine执行任务
for i := 0; i < taskCount; i++ {
go handleSingleTask(i, &wg)
}
// 阻塞等待所有任务完成
wg.Wait()
fmt.Println("所有任务执行完毕,主程序继续运行")
}
goroutine多任务管理技巧
任务结果收集
很多时候我们需要获取每个goroutine的执行结果,这时候可以使用channel来传递结果,避免多个goroutine同时操作共享变量引发数据竞争问题。
以下示例演示了多个goroutine计算任务结果并统一收集的过程:
package main
import (
"fmt"
"sync"
)
// 计算任务,将结果发送到channel
func calculateTask(taskID int, resultChan chan<- int, wg *sync.WaitGroup) {
defer wg.Done()
// 模拟计算逻辑,这里简单返回任务ID的2倍
result := taskID * 2
resultChan <- result
}
func main() {
var wg sync.WaitGroup
taskCount := 4
// 创建带缓冲的channel,缓冲大小和任务数一致避免发送阻塞
resultChan := make(chan int, taskCount)
wg.Add(taskCount)
// 启动多个计算goroutine
for i := 0; i < taskCount; i++ {
go calculateTask(i, resultChan, &wg)
}
// 等待所有任务完成后关闭channel
go func() {
wg.Wait()
close(resultChan)
}()
// 从channel中读取所有结果
total := 0
for res := range resultChan {
fmt.Printf("收到任务结果:%dn", res)
total += res
}
fmt.Printf("所有结果总和为:%dn", total)
}
goroutine异常控制
goroutine中如果出现panic没有被捕获,会导致整个程序崩溃,因此需要在goroutine内部添加recover逻辑来处理异常,保证单个任务的异常不会影响其他任务执行。
异常处理的示例代码:
package main
import (
"fmt"
"sync"
)
func safeTask(taskID int, wg *sync.WaitGroup) {
defer wg.Done()
// 捕获goroutine内部的panic
defer func() {
if err := recover(); err != nil {
fmt.Printf("任务 %d 执行出现异常:%vn", taskID, err)
}
}()
// 模拟某个任务出现异常
if taskID == 2 {
panic("任务2模拟异常")
}
fmt.Printf("任务 %d 正常执行完成n", taskID)
}
func main() {
var wg sync.WaitGroup
taskCount := 5
wg.Add(taskCount)
for i := 0; i < taskCount; i++ {
go safeTask(i, &wg)
}
wg.Wait()
fmt.Println("所有任务处理完毕,程序正常退出")
}
注意事项
- 不要随意创建大量无限制的goroutine,避免占用过多系统资源,必要时可以使用goroutine池来控制并发数量。
- 访问共享资源时必须添加同步机制,比如使用
sync.Mutex或者channel来避免数据竞争。 - 主程序退出时不会等待未执行完的goroutine,一定要通过
WaitGroup或者channel等方式确保所有任务完成或者提前终止。