跳转到内容

Go 并发概述

来自代码酷


介绍[编辑 | 编辑源代码]

Go语言(Golang)以其内置的并发支持而闻名,这是其核心设计哲学之一。与其他语言依赖操作系统线程或第三方库不同,Go通过goroutinechannel提供原生的并发模型。本章将介绍Go并发的基本概念、工作原理及其优势。

什么是并发?[编辑 | 编辑源代码]

在计算机科学中,并发(Concurrency)是指程序能够同时处理多个任务的能力。与并行(Parallelism)不同,并发强调任务的逻辑独立性,而并行强调物理上的同时执行。Go的并发模型基于CSP(Communicating Sequential Processes)理论,通过轻量级的goroutine和通信机制实现高效的任务管理。

Goroutine:轻量级线程[编辑 | 编辑源代码]

Goroutine是Go并发的基本单元,由Go运行时管理。与传统线程相比,其启动开销极小(通常仅2KB栈空间),且可动态扩展。

基本语法[编辑 | 编辑源代码]

通过`go`关键字即可启动一个goroutine:

package main

import (
    "fmt"
    "time"
)

func sayHello() {
    fmt.Println("Hello from goroutine!")
}

func main() {
    go sayHello() // 启动goroutine
    time.Sleep(1 * time.Second) // 等待goroutine完成
    fmt.Println("Main function exits.")
}

输出:

Hello from goroutine!
Main function exits.

Goroutine调度[编辑 | 编辑源代码]

Go运行时使用M:N调度模型,将goroutine映射到少量操作系统线程上。通过工作窃取算法实现负载均衡:

flowchart LR G1[Goroutine1] --> M1[Thread1] G2[Goroutine2] --> M1 G3[Goroutine3] --> M2[Thread2] M1 --> Scheduler M2 --> Scheduler

Channel:通信机制[编辑 | 编辑源代码]

Channel是goroutine间的类型安全通信管道,遵循"不要通过共享内存通信,而要通过通信共享内存"的原则。

基本操作[编辑 | 编辑源代码]

ch := make(chan int) // 创建无缓冲channel
go func() { ch <- 42 }() // 发送数据
value := <-ch // 接收数据

缓冲与无缓冲[编辑 | 编辑源代码]

Channel类型对比
类型 声明方式 行为
make(chan T) | 同步通信,发送/接收会阻塞
make(chan T, size) | 异步通信,缓冲区满时阻塞

同步原语[编辑 | 编辑源代码]

Go标准库提供多种同步工具:

  • sync.Mutex:互斥锁
  • sync.WaitGroup:等待一组goroutine完成
  • context.Context:跨goroutine取消信号

WaitGroup示例[编辑 | 编辑源代码]

package main

import (
    "fmt"
    "sync"
)

func worker(id int, wg *sync.WaitGroup) {
    defer wg.Done()
    fmt.Printf("Worker %d starting\n", id)
    // 模拟工作
    fmt.Printf("Worker %d done\n", id)
}

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

实际应用案例[编辑 | 编辑源代码]

Web服务器请求处理[编辑 | 编辑源代码]

典型的高并发场景,每个请求独立处理:

func handleRequest(w http.ResponseWriter, r *http.Request) {
    // 处理逻辑
}

func main() {
    http.HandleFunc("/", handleRequest)
    log.Fatal(http.ListenAndServe(":8080", nil))
}

生产者-消费者模式[编辑 | 编辑源代码]

flowchart LR Producer -->|数据| Channel --> Consumer

func producer(ch chan<- int) {
    for i := 0; i < 5; i++ {
        ch <- i
    }
    close(ch)
}

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

并发模式[编辑 | 编辑源代码]

  • Fan-out/Fan-in:多个goroutine处理输入后合并结果
  • Pipeline:串联处理阶段
  • Worker Pool:固定数量的goroutine处理任务队列

Worker Pool实现[编辑 | 编辑源代码]

func worker(id int, jobs <-chan int, results chan<- int) {
    for j := range jobs {
        fmt.Printf("Worker %d processing job %d\n", id, j)
        results <- j * 2
    }
}

func main() {
    jobs := make(chan int, 100)
    results := make(chan int, 100)

    // 启动3个worker
    for w := 1; w <= 3; w++ {
        go worker(w, jobs, results)
    }

    // 发送任务
    for j := 1; j <= 5; j++ {
        jobs <- j
    }
    close(jobs)

    // 收集结果
    for a := 1; a <= 5; a++ {
        <-results
    }
}

性能考量[编辑 | 编辑源代码]

  • 避免过度创建goroutine(考虑使用pool)
  • 注意channel的阻塞行为
  • 使用runtime.NumGoroutine()监控数量
  • 利用-race标志检测数据竞争

数学基础[编辑 | 编辑源代码]

Amdahl定律描述并发加速比: S=1(1p)+pn 其中:

  • S:加速比
  • p:可并行部分比例
  • n:处理器数量

总结[编辑 | 编辑源代码]

Go的并发模型通过goroutine和channel提供了一种高效、安全的并发编程方式。关键要点:

  • Goroutine比线程更轻量
  • Channel确保安全通信
  • 标准库提供丰富的同步工具
  • 多种模式可应对不同场景

页面模块:Message box/ambox.css没有内容。