Goroutine基础 #
一、Goroutine概述 #
Goroutine是Go语言的轻量级线程,由Go运行时管理。
1.1 特点 #
- 轻量级:初始栈仅2KB
- 低开销:创建和销毁成本低
- 由Go运行时调度
- 支持数百万个goroutine
二、创建Goroutine #
2.1 使用go关键字 #
go
go 函数调用
2.2 启动匿名函数 #
go
go func() {
fmt.Println("Hello from goroutine")
}()
2.3 启动命名函数 #
go
func printMessage(msg string) {
fmt.Println(msg)
}
func main() {
go printMessage("Hello")
time.Sleep(time.Second)
}
2.4 带参数的goroutine #
go
func printNumber(n int) {
fmt.Println(n)
}
func main() {
for i := 0; i < 5; i++ {
go func(n int) {
fmt.Println(n)
}(i)
}
time.Sleep(time.Second)
}
三、Goroutine特性 #
3.1 并发执行 #
go
func main() {
go func() {
fmt.Println("Goroutine 1")
}()
go func() {
fmt.Println("Goroutine 2")
}()
fmt.Println("Main")
time.Sleep(time.Second)
}
3.2 非阻塞 #
go
func slowOperation() {
time.Sleep(2 * time.Second)
fmt.Println("Slow operation done")
}
func main() {
go slowOperation()
fmt.Println("Main continues")
time.Sleep(3 * time.Second)
}
3.3 共享内存 #
go
var counter int
func increment() {
counter++
}
func main() {
for i := 0; i < 1000; i++ {
go increment()
}
time.Sleep(time.Second)
fmt.Println(counter) // 可能不是1000(竞态条件)
}
四、等待Goroutine #
4.1 time.Sleep(不推荐) #
go
func main() {
go func() {
fmt.Println("Working...")
}()
time.Sleep(time.Second) // 不精确
}
4.2 sync.WaitGroup #
go
var wg sync.WaitGroup
func worker() {
defer wg.Done()
fmt.Println("Working...")
}
func main() {
wg.Add(1)
go worker()
wg.Wait()
}
4.3 多个goroutine #
go
func worker(id int) {
defer wg.Done()
fmt.Printf("Worker %d\n", id)
}
var wg sync.WaitGroup
func main() {
for i := 0; i < 5; i++ {
wg.Add(1)
go worker(i)
}
wg.Wait()
}
五、Goroutine vs 线程 #
| 特性 | Goroutine | 线程 |
|---|---|---|
| 栈大小 | 2KB(可增长) | 1-8MB |
| 创建开销 | 低 | 高 |
| 调度 | Go运行时 | 操作系统 |
| 切换开销 | 低 | 高 |
| 数量限制 | 数百万 | 数千 |
六、GMP模型 #
6.1 概念 #
- G (Goroutine):协程
- M (Machine):系统线程
- P (Processor):处理器
6.2 调度原理 #
text
G ──┐
G ──┼── P ── M ── CPU
G ──┘
6.3 默认设置 #
go
fmt.Println(runtime.GOMAXPROCS(0)) // 默认等于CPU核心数
七、runtime包 #
7.1 GOMAXPROCS #
go
runtime.GOMAXPROCS(4) // 设置使用的CPU核心数
7.2 NumGoroutine #
go
fmt.Println(runtime.NumGoroutine()) // 当前goroutine数量
7.3 Gosched #
go
runtime.Gosched() // 让出CPU
7.4 Goexit #
go
func worker() {
defer fmt.Println("Cleanup")
runtime.Goexit() // 终止当前goroutine
fmt.Println("Never reached")
}
八、实际应用 #
8.1 并发下载 #
go
func download(url string, wg *sync.WaitGroup) {
defer wg.Done()
resp, _ := http.Get(url)
defer resp.Body.Close()
fmt.Printf("Downloaded: %s\n", url)
}
func main() {
urls := []string{
"https://example.com/1",
"https://example.com/2",
"https://example.com/3",
}
var wg sync.WaitGroup
for _, url := range urls {
wg.Add(1)
go download(url, &wg)
}
wg.Wait()
}
8.2 并发处理 #
go
func process(item int, wg *sync.WaitGroup) {
defer wg.Done()
time.Sleep(time.Millisecond * 100)
fmt.Printf("Processed: %d\n", item)
}
func main() {
items := make([]int, 10)
var wg sync.WaitGroup
for _, item := range items {
wg.Add(1)
go process(item, &wg)
}
wg.Wait()
}
九、常见错误 #
9.1 闭包变量问题 #
go
for i := 0; i < 3; i++ {
go func() {
fmt.Println(i) // 可能输出 3 3 3
}()
}
// 正确做法
for i := 0; i < 3; i++ {
go func(n int) {
fmt.Println(n)
}(i)
}
9.2 主程序退出 #
go
func main() {
go func() {
fmt.Println("Working...")
}()
// 主程序立即退出,goroutine可能未执行
}
9.3 竞态条件 #
go
var counter int
func main() {
for i := 0; i < 1000; i++ {
go func() {
counter++ // 竞态条件
}()
}
}
十、最佳实践 #
10.1 使用WaitGroup等待 #
go
var wg sync.WaitGroup
wg.Add(1)
go func() {
defer wg.Done()
// 工作
}()
wg.Wait()
10.2 避免全局变量 #
go
// 不好
var counter int
// 好
func worker(counter *int) {}
10.3 限制并发数 #
go
sem := make(chan struct{}, 10) // 最多10个并发
for _, item := range items {
sem <- struct{}{}
go func(item int) {
defer func() { <-sem }()
process(item)
}(item)
}
十一、总结 #
Goroutine要点:
| 特性 | 说明 |
|---|---|
| 创建 | go关键字 |
| 轻量 | 2KB初始栈 |
| 调度 | Go运行时 |
| 等待 | sync.WaitGroup |
关键点:
- 轻量级:可创建数百万goroutine
- 并发执行:多个goroutine并发运行
- 等待机制:使用WaitGroup等待完成
- 闭包陷阱:注意循环变量问题
- 竞态条件:共享数据需要同步
准备好学习Goroutine调度了吗?让我们进入下一章!
最后更新:2026-03-26