Skip to content

第 15 章:并发编程(Go 最大优势)

15.1 什么是并发?goroutine 是什么?

什么是并发?

并发是指多个任务在同一时间间隔内执行,而不是同一时刻执行。在 Go 语言中,并发是通过 goroutine 实现的。

goroutine 是什么?

goroutine 是 Go 语言中的轻量级线程,由 Go 运行时管理。它的特点是:

  • 轻量:创建一个 goroutine 的成本很低,只需要几 KB 的栈空间
  • 高效:Go 运行时会自动调度 goroutine
  • 并发:多个 goroutine 可以同时执行

15.2 启动 goroutine(最简单的并发)

基本用法

使用 go 关键字启动一个 goroutine:

go
package main

import (
    "fmt"
    "time"
)

func sayHello() {
    for i := 0; i < 5; i++ {
        fmt.Println("Hello")
        time.Sleep(time.Millisecond * 100)
    }
}

func main() {
    // 启动一个 goroutine
    go sayHello()
    
    // 主 goroutine 继续执行
    for i := 0; i < 5; i++ {
        fmt.Println("World")
        time.Sleep(time.Millisecond * 100)
    }
}

带参数的 goroutine

go
func sayHello(name string) {
    fmt.Println("Hello, " + name)
}

func main() {
    go sayHello("Alice")
    go sayHello("Bob")
    
    // 等待 goroutine 执行完成
    time.Sleep(time.Second)
}

15.3 channel 通道:goroutine 通信

channel 是 goroutine 之间的通信机制,用于在 goroutine 之间传递数据。

创建 channel

go
// 创建一个整型 channel
ch := make(chan int)

// 创建一个字符串 channel
ch := make(chan string)

发送数据到 channel

go
ch <- 42 // 发送 42 到 channel
ch <- "hello" // 发送 "hello" 到 channel

从 channel 接收数据

go
value := <-ch // 从 channel 接收数据

// 同时判断是否关闭
value, ok := <-ch
if !ok {
    fmt.Println("Channel closed")
}

示例:使用 channel 进行通信

go
package main

import "fmt"

func producer(ch chan<- int) {
    for i := 0; i < 5; i++ {
        ch <- i // 发送数据
        fmt.Println("Produced:", i)
    }
    close(ch) // 关闭 channel
}

func consumer(ch <-chan int) {
    for value := range ch {
        fmt.Println("Consumed:", value)
    }
}

func main() {
    ch := make(chan int)
    
    go producer(ch)
    go consumer(ch)
    
    // 等待 goroutine 执行完成
    var input string
    fmt.Scanln(&input)
}

15.4 无缓冲 / 有缓冲 channel

无缓冲 channel

无缓冲 channel 会阻塞发送方,直到接收方接收数据:

go
ch := make(chan int) // 无缓冲 channel

// 发送方会阻塞,直到接收方接收
ch <- 42

// 接收方会阻塞,直到发送方发送
value := <-ch

有缓冲 channel

有缓冲 channel 有一个缓冲区,当缓冲区未满时,发送方不会阻塞;当缓冲区未空时,接收方不会阻塞:

go
ch := make(chan int, 3) // 有缓冲 channel,缓冲区大小为 3

// 可以连续发送 3 个数据而不会阻塞
ch <- 1
ch <- 2
ch <- 3

// 发送第 4 个数据时会阻塞
// ch <- 4

// 接收数据
value1 := <-ch
value2 := <-ch
value3 := <-ch

15.5 select 语句(多路复用)

select 语句用于在多个 channel 操作中选择一个执行:

基本用法

go
select {
case value := <-ch1:
    fmt.Println("Received from ch1:", value)
case ch2 <- 42:
    fmt.Println("Sent to ch2")
case <-time.After(time.Second):
    fmt.Println("Timeout")
default:
    fmt.Println("No operation ready")
}

示例:使用 select 处理多个 channel

go
package main

import (
    "fmt"
    "time"
)

func main() {
    ch1 := make(chan string)
    ch2 := make(chan string)
    
    go func() {
        time.Sleep(time.Millisecond * 500)
        ch1 <- "Hello from ch1"
    }()
    
    go func() {
        time.Sleep(time.Millisecond * 300)
        ch2 <- "Hello from ch2"
    }()
    
    for i := 0; i < 2; i++ {
        select {
        case msg1 := <-ch1:
            fmt.Println(msg1)
        case msg2 := <-ch2:
            fmt.Println(msg2)
        }
    }
}

15.6 并发安全与 sync 包(锁)

并发安全问题

当多个 goroutine 同时访问共享资源时,会产生并发安全问题:

go
package main

import (
    "fmt"
    "sync"
    "time"
)

var counter int

func increment() {
    for i := 0; i < 1000; i++ {
        counter++
    }
}

func main() {
    var wg sync.WaitGroup
    
    for i := 0; i < 10; i++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            increment()
        }()
    }
    
    wg.Wait()
    fmt.Println("Counter:", counter) // 可能不是 10000
}

使用互斥锁

go
package main

import (
    "fmt"
    "sync"
    "time"
)

var (
    counter int
    mu      sync.Mutex
)

func increment() {
    for i := 0; i < 1000; i++ {
        mu.Lock()   // 加锁
        counter++
        mu.Unlock() // 解锁
    }
}

func main() {
    var wg sync.WaitGroup
    
    for i := 0; i < 10; i++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            increment()
        }()
    }
    
    wg.Wait()
    fmt.Println("Counter:", counter) // 总是 10000
}

使用 sync.WaitGroup

go
package main

import (
    "fmt"
    "sync"
    "time"
)

func worker(id int, wg *sync.WaitGroup) {
    defer wg.Done()
    fmt.Printf("Worker %d starting\n", id)
    time.Sleep(time.Second)
    fmt.Printf("Worker %d done\n", id)
}

func main() {
    var wg sync.WaitGroup
    
    for i := 1; i <= 5; i++ {
        wg.Add(1)
        go worker(i, &wg)
    }
    
    fmt.Println("Waiting for workers...")
    wg.Wait()
    fmt.Println("All workers done")
}

© 2026 编程马·菜鸟教程 版权所有