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

关键点:

  1. 轻量级:可创建数百万goroutine
  2. 并发执行:多个goroutine并发运行
  3. 等待机制:使用WaitGroup等待完成
  4. 闭包陷阱:注意循环变量问题
  5. 竞态条件:共享数据需要同步

准备好学习Goroutine调度了吗?让我们进入下一章!

最后更新:2026-03-26