Go语言-->Goroutine 详细解释
Goroutine 详细解释
goroutine 是 Go 语言中的轻量级线程,由 Go 运行时管理。它是实现并发编程的核心机制。
核心概念
| 特性 | 说明 |
|---|---|
| 轻量级 | 创建成本极低,一个程序可以轻松创建数百万个 goroutine |
| 并发执行 | 多个 goroutine 可以同时运行 |
| 由运行时管理 | Go 运行时自动调度 goroutine 到 CPU 核心上 |
| 非抢占式 | goroutine 只在特定点(如 I/O、channel 操作)让出控制权 |
基本用法
1. 创建 Goroutine
package mainimport ("fmt""time"
)func main() {// 普通函数调用(同步)sayHello("Alice")// 启动 goroutine(异步)go sayHello("Bob")// 主 goroutine 需要等待子 goroutine 完成time.Sleep(1 * time.Second)
}func sayHello(name string) {fmt.Printf("Hello, %s\n", name)
}
输出:
Hello, Alice
Hello, Bob
2. 多个 Goroutine
package mainimport ("fmt""time"
)func main() {for i := 1; i <= 5; i++ {go func(id int) {fmt.Printf("Goroutine %d 开始\n", id)time.Sleep(time.Duration(id) * time.Second)fmt.Printf("Goroutine %d 完成\n", id)}(i)}// 等待所有 goroutine 完成time.Sleep(6 * time.Second)fmt.Println("主程序结束")
}
输出:
Goroutine 1 开始
Goroutine 2 开始
Goroutine 3 开始
Goroutine 4 开始
Goroutine 5 开始
Goroutine 1 完成
Goroutine 2 完成
Goroutine 3 完成
Goroutine 4 完成
Goroutine 5 完成
主程序结束
go func() 和 go sayHello("Bob") 的区别
| 特性 | go func() | go sayHello("Bob") |
|---|---|---|
| 类型 | 匿名函数 | 命名函数 |
| 定义位置 | 调用处定义 | 提前定义 |
| 代码量 | 适合简短逻辑 | 适合复杂逻辑 |
| 可重用性 | 不可重用 | 可重用 |
| 闭包 | 可访问外部变量 | 需要参数传递 |
Goroutine vs 线程
| 特性 | Goroutine | 线程 |
|---|---|---|
| 内存占用 | ~2KB | ~1-2MB |
| 创建速度 | 极快 | 较慢 |
| 数量 | 可创建百万级 | 通常数百个 |
| 切换成本 | 低 | 高 |
| 管理 | Go 运行时 | 操作系统 |
与 Channel 结合
package mainimport ("fmt"
)func main() {// 创建 channelresults := make(chan string)// 启动 goroutinego func() {results <- "任务 1 完成"}()go func() {results <- "任务 2 完成"}()// 接收结果fmt.Println(<-results)fmt.Println(<-results)
}
输出:
任务 1 完成
任务 2 完成
常见模式
1. 使用 sync.WaitGroup 等待完成
package mainimport ("fmt""sync"
)func main() {var wg sync.WaitGroupfor i := 1; i <= 3; i++ {wg.Add(1)go func(id int) {defer wg.Done()fmt.Printf("Goroutine %d 执行\n", id)}(i)}wg.Wait()fmt.Println("所有 goroutine 完成")
}
2. 并发 HTTP 请求
package mainimport ("fmt""net/http""sync"
)func main() {urls := []string{"https://example.com","https://google.com","https://github.com",}var wg sync.WaitGroupfor _, url := range urls {wg.Add(1)go func(u string) {defer wg.Done()resp, err := http.Get(u)if err != nil {fmt.Printf("请求 %s 失败: %v\n", u, err)} else {fmt.Printf("请求 %s 成功,状态码: %d\n", u, resp.StatusCode)}}(url)}wg.Wait()
}
注意事项
⚠️ 常见问题:
- 主 goroutine 过早退出:子 goroutine 还未完成就结束了
- 竞态条件:多个 goroutine 同时修改共享变量
- 死锁:goroutine 互相等待
✅ 最佳实践:
- 使用
sync.WaitGroup或channel等待 goroutine 完成 - 使用
sync.Mutex保护共享数据 - 避免在 goroutine 中直接修改外部变量(使用参数传递)
