并发编程
Go 语言的并发模型是其最重要的特性之一,通过 Goroutine 和 Channel 实现优雅的并发编程。
Goroutine
Goroutine 是 Go 语言的轻量级线程,由 Go 运行时管理。
创建 Goroutine
使用 go 关键字创建 Goroutine:
go
func sayHello() {
fmt.Println("Hello")
}
func main() {
// 启动一个新的 Goroutine
go sayHello()
// 主 Goroutine 继续执行
fmt.Println("World")
// 等待一下,让 Goroutine 有时间执行
time.Sleep(100 * time.Millisecond)
}匿名函数 Goroutine
go
go func() {
fmt.Println("匿名函数 Goroutine")
}()
// 带参数
go func(msg string) {
fmt.Println(msg)
}("Hello from Goroutine")多个 Goroutine
go
func printNumber(n int) {
for i := 0; i < n; i++ {
fmt.Printf("Goroutine %d: %d\n", n, i)
time.Sleep(100 * time.Millisecond)
}
}
func main() {
go printNumber(1)
go printNumber(2)
go printNumber(3)
time.Sleep(2 * time.Second)
}Channel
Channel 是 Goroutine 之间通信的管道。
创建 Channel
go
// 无缓冲 Channel
ch := make(chan int)
// 有缓冲 Channel
ch := make(chan int, 10)发送和接收
go
ch := make(chan string)
// 发送数据
go func() {
ch <- "Hello"
}()
// 接收数据
msg := <-ch
fmt.Println(msg)无缓冲 Channel
无缓冲 Channel 是同步的,发送和接收必须同时准备好:
go
ch := make(chan int)
go func() {
ch <- 42 // 发送
}()
value := <-ch // 接收
fmt.Println(value)有缓冲 Channel
有缓冲 Channel 可以存储多个值:
go
ch := make(chan int, 3)
ch <- 1
ch <- 2
ch <- 3
fmt.Println(<-ch) // 1
fmt.Println(<-ch) // 2
fmt.Println(<-ch) // 3Channel 方向
可以指定 Channel 的方向:
go
// 只发送
func sendOnly(ch chan<- int) {
ch <- 42
}
// 只接收
func receiveOnly(ch <-chan int) {
value := <-ch
fmt.Println(value)
}关闭 Channel
go
ch := make(chan int)
go func() {
for i := 0; i < 5; i++ {
ch <- i
}
close(ch) // 关闭 Channel
}()
// 接收直到 Channel 关闭
for value := range ch {
fmt.Println(value)
}
// 检查 Channel 是否关闭
value, ok := <-ch
if !ok {
fmt.Println("Channel 已关闭")
}Select
select 语句用于在多个 Channel 操作中选择一个执行。
基本用法
go
ch1 := make(chan string)
ch2 := make(chan string)
go func() {
time.Sleep(1 * time.Second)
ch1 <- "来自 ch1"
}()
go func() {
time.Sleep(2 * time.Second)
ch2 <- "来自 ch2"
}()
select {
case msg1 := <-ch1:
fmt.Println(msg1)
case msg2 := <-ch2:
fmt.Println(msg2)
}默认分支
go
select {
case msg := <-ch:
fmt.Println(msg)
default:
fmt.Println("没有消息")
}超时处理
go
select {
case msg := <-ch:
fmt.Println(msg)
case <-time.After(1 * time.Second):
fmt.Println("超时")
}多个 Channel
go
for {
select {
case msg1 := <-ch1:
fmt.Println("ch1:", msg1)
case msg2 := <-ch2:
fmt.Println("ch2:", msg2)
case <-done:
return
}
}同步原语
sync.Mutex
互斥锁用于保护共享资源:
go
import "sync"
var (
counter int
mutex sync.Mutex
)
func increment() {
mutex.Lock()
defer mutex.Unlock()
counter++
}
func main() {
var wg sync.WaitGroup
for i := 0; i < 1000; i++ {
wg.Add(1)
go func() {
defer wg.Done()
increment()
}()
}
wg.Wait()
fmt.Println("Counter:", counter)
}sync.RWMutex
读写锁,允许多个读操作或一个写操作:
go
var (
data map[string]string
rwMutex sync.RWMutex
)
func read(key string) string {
rwMutex.RLock()
defer rwMutex.RUnlock()
return data[key]
}
func write(key, value string) {
rwMutex.Lock()
defer rwMutex.Unlock()
data[key] = value
}sync.WaitGroup
等待一组 Goroutine 完成:
go
var wg sync.WaitGroup
func worker(id int) {
defer wg.Done()
fmt.Printf("Worker %d 开始\n", id)
time.Sleep(1 * time.Second)
fmt.Printf("Worker %d 完成\n", id)
}
func main() {
for i := 1; i <= 3; i++ {
wg.Add(1)
go worker(i)
}
wg.Wait()
fmt.Println("所有 Worker 完成")
}sync.Once
确保某个操作只执行一次:
go
var once sync.Once
func setup() {
fmt.Println("初始化")
}
func main() {
once.Do(setup)
once.Do(setup) // 不会执行
}并发模式
Worker Pool
go
func worker(id int, jobs <-chan int, results chan<- int) {
for job := range jobs {
fmt.Printf("Worker %d 处理任务 %d\n", id, job)
time.Sleep(1 * time.Second)
results <- job * 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 r := 1; r <= 5; r++ {
fmt.Println("结果:", <-results)
}
}管道模式
go
func numbers() <-chan int {
out := make(chan int)
go func() {
for i := 0; i < 5; i++ {
out <- i
}
close(out)
}()
return out
}
func square(in <-chan int) <-chan int {
out := make(chan int)
go func() {
for n := range in {
out <- n * n
}
close(out)
}()
return out
}
func main() {
nums := numbers()
squares := square(nums)
for s := range squares {
fmt.Println(s)
}
}完整示例
go
package main
import (
"fmt"
"sync"
"time"
)
func main() {
// Channel 示例
ch := make(chan string, 2)
go func() {
ch <- "消息 1"
ch <- "消息 2"
close(ch)
}()
for msg := range ch {
fmt.Println(msg)
}
// WaitGroup 示例
var wg sync.WaitGroup
for i := 1; i <= 3; i++ {
wg.Add(1)
go func(id int) {
defer wg.Done()
fmt.Printf("Goroutine %d 执行\n", id)
time.Sleep(100 * time.Millisecond)
}(i)
}
wg.Wait()
fmt.Println("所有 Goroutine 完成")
}最佳实践
- 使用 Channel 进行通信,而不是共享内存
- 避免在 Goroutine 之间共享可变状态
- 使用 Context 管理 Goroutine 生命周期
- 合理使用缓冲 Channel 提高性能
- 注意 Goroutine 泄漏问题
下一步
接下来我们将学习:
