Go 并发概述
外观
介绍[编辑 | 编辑源代码]
Go语言(Golang)以其内置的并发支持而闻名,这是其核心设计哲学之一。与其他语言依赖操作系统线程或第三方库不同,Go通过goroutine和channel提供原生的并发模型。本章将介绍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映射到少量操作系统线程上。通过工作窃取算法实现负载均衡:
Channel:通信机制[编辑 | 编辑源代码]
Channel是goroutine间的类型安全通信管道,遵循"不要通过共享内存通信,而要通过通信共享内存"的原则。
基本操作[编辑 | 编辑源代码]
ch := make(chan int) // 创建无缓冲channel
go func() { ch <- 42 }() // 发送数据
value := <-ch // 接收数据
缓冲与无缓冲[编辑 | 编辑源代码]
类型 | 声明方式 | 行为 |
---|---|---|
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))
}
生产者-消费者模式[编辑 | 编辑源代码]
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定律描述并发加速比: 其中:
- :加速比
- :可并行部分比例
- :处理器数量
总结[编辑 | 编辑源代码]
Go的并发模型通过goroutine和channel提供了一种高效、安全的并发编程方式。关键要点:
- Goroutine比线程更轻量
- Channel确保安全通信
- 标准库提供丰富的同步工具
- 多种模式可应对不同场景
页面模块:Message box/ambox.css没有内容。
注意:并发程序需特别注意资源竞争和死锁问题,务必使用 -race 参数进行测试 |