Goroutine 是 Go 语言并发编程的核心特性。本章将介绍 Goroutine 的基本概念和使用方法。
Goroutine 是 Go 语言中的轻量级线程,由 Go 运行时管理。
Goroutine vs 线程:
┌─────────────────────────────────────────────────┐
│ Goroutine │
├─────────────────────────────────────────────────┤
│ 初始栈大小: 2KB(可动态增长) │
│ 创建开销: 微秒级 │
│ 切换开销: 用户态切换,无需内核参与 │
│ 调度: Go 运行时调度 │
│ 数量: 可以轻松创建百万个 │
└─────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────┐
│ 操作系统线程 │
├─────────────────────────────────────────────────┤
│ 初始栈大小: 1MB │
│ 创建开销: 毫秒级 │
│ 切换开销: 需要内核参与 │
│ 调度: 操作系统调度 │
│ 数量: 通常几千个 │
└─────────────────────────────────────────────────┘
package main
import (
"fmt"
"time"
)
func sayHello() {
fmt.Println("Hello from goroutine!")
}
func main() {
go sayHello()
fmt.Println("Hello from main!")
time.Sleep(time.Second)
}
package main
import (
"fmt"
"time"
)
func main() {
go func() {
fmt.Println("匿名函数 goroutine")
}()
go func(name string) {
fmt.Printf("带参数: %s\n", name)
}("张三")
time.Sleep(time.Second)
}
main 函数运行在主 goroutine 中,当主 goroutine 结束,所有其他 goroutine 也会结束:
package main
import (
"fmt"
"time"
)
func main() {
go func() {
time.Sleep(2 * time.Second)
fmt.Println("这行可能不会输出")
}()
time.Sleep(time.Second)
fmt.Println("main 结束")
}
使用 runtime.Gosched() 让出 CPU:
package main
import (
"fmt"
"runtime"
)
func main() {
go func() {
for i := 0; i < 5; i++ {
fmt.Printf("A: %d\n", i)
runtime.Gosched()
}
}()
for i := 0; i < 5; i++ {
fmt.Printf("B: %d\n", i)
runtime.Gosched()
}
}
package main
import (
"fmt"
"runtime"
"time"
)
func main() {
fmt.Printf("初始 goroutine 数量: %d\n", runtime.NumGoroutine())
for i := 0; i < 10; i++ {
go func(n int) {
time.Sleep(time.Second)
fmt.Printf("goroutine %d 完成\n", n)
}(i)
}
fmt.Printf("创建后 goroutine 数量: %d\n", runtime.NumGoroutine())
time.Sleep(2 * time.Second)
fmt.Printf("完成后 goroutine 数量: %d\n", runtime.NumGoroutine())
}
package main
import (
"fmt"
"runtime"
"time"
)
func worker(id int) {
for i := 0; i < 3; i++ {
fmt.Printf("worker %d: %d\n", id, i)
time.Sleep(100 * time.Millisecond)
}
}
func main() {
runtime.GOMAXPROCS(2)
for i := 1; i <= 3; i++ {
go worker(i)
}
time.Sleep(time.Second)
}
package main
import (
"fmt"
"sync"
"time"
)
func worker(id int, wg *sync.WaitGroup) {
defer wg.Done()
fmt.Printf("worker %d 开始\n", id)
time.Sleep(time.Second)
fmt.Printf("worker %d 完成\n", id)
}
func main() {
var wg sync.WaitGroup
for i := 1; i <= 5; i++ {
wg.Add(1)
go worker(i, &wg)
}
wg.Wait()
fmt.Println("所有 worker 完成")
}
package main
import (
"fmt"
"time"
)
func main() {
for i := 0; i < 3; i++ {
go func() {
fmt.Printf("i = %d\n", i)
}()
}
time.Sleep(time.Second)
}
输出可能是:
i = 3
i = 3
i = 3
解决方法:传递参数
package main
import (
"fmt"
"time"
)
func main() {
for i := 0; i < 3; i++ {
go func(n int) {
fmt.Printf("n = %d\n", n)
}(i)
}
time.Sleep(time.Second)
}
package main
import (
"fmt"
"sync"
"time"
)
func download(url string, wg *sync.WaitGroup) {
defer wg.Done()
fmt.Printf("开始下载: %s\n", url)
time.Sleep(time.Second)
fmt.Printf("下载完成: %s\n", url)
}
func main() {
urls := []string{
"https://example.com/file1",
"https://example.com/file2",
"https://example.com/file3",
}
var wg sync.WaitGroup
start := time.Now()
for _, url := range urls {
wg.Add(1)
go download(url, &wg)
}
wg.Wait()
fmt.Printf("总耗时: %v\n", time.Since(start))
}
本章学习了 Go 语言的 Goroutine:
| 知识点 | 说明 |
|---|---|
| 创建 | go func() |
| 轻量 | 初始 2KB 栈,可动态增长 |
| 调度 | Go 运行时调度 |
| WaitGroup | 等待多个 goroutine 完成 |
| 闭包陷阱 | 循环变量需要传参 |