1. 并发编程基础概念
并发编程是现代软件开发中不可或缺的技能。如果说传统的顺序执行像是一个人按部就班地完成任务,那么并发就像是同时指挥多个工人并行工作。Go语言天生支持并发,让并发编程变得简单而高效。
1.1 什么是并发
让我们通过生活中的例子来理解并发:
现实场景对比:
- 顺序执行:一个人做饭 → 洗碗 → 打扫卫生(总共需要3小时)
- 并发执行:一个人做饭的同时,另一个人洗碗,第三人打扫卫生(总共只需1小时)
程序中的并发:
- 顺序执行:执行任务A → 执行任务B → 执行任务C
- 并发执行:同时执行任务A、B、C
1.2 并发 vs 并行 vs 串行
go
package main
import (
"fmt"
"time"
)
// 串行执行示例
func serialExecution() {
fmt.Println("=== 串行执行 ===")
start := time.Now()
task1()
task2()
task3()
elapsed := time.Since(start)
fmt.Printf("串行执行总耗时:%v\n\n", elapsed)
}
// 并发执行示例
func concurrentExecution() {
fmt.Println("=== 并发执行 ===")
start := time.Now()
// 启动3个goroutine并发执行
go task1()
go task2()
go task3()
// 等待一段时间让goroutine执行完成
time.Sleep(2 * time.Second)
elapsed := time.Since(start)
fmt.Printf("并发执行总耗时:%v\n\n", elapsed)
}
// 模拟耗时任务
func task1() {
fmt.Println("任务1开始执行...")
time.Sleep(1 * time.Second)
fmt.Println("任务1执行完成")
}
func task2() {
fmt.Println("任务2开始执行...")
time.Sleep(1 * time.Second)
fmt.Println("任务2执行完成")
}
func task3() {
fmt.Println("任务3开始执行...")
time.Sleep(1 * time.Second)
fmt.Println("任务3执行完成")
}
func main() {
serialExecution()
concurrentExecution()
// 解释并发的优势
fmt.Println("=== 并发优势说明 ===")
fmt.Println("1. 提高程序执行效率")
fmt.Println("2. 更好地利用多核CPU")
fmt.Println("3. 改善用户体验(响应更快)")
fmt.Println("4. 处理I/O密集型任务更有效")
}
运行结果:
bash
=== 串行执行 ===
任务1开始执行...
任务1执行完成
任务2开始执行...
任务2执行完成
任务3开始执行...
任务3执行完成
串行执行总耗时:3.004s
=== 并发执行 ===
任务1开始执行...
任务2开始执行...
任务3开始执行...
任务1执行完成
任务2执行完成
任务3执行完成
并发执行总耗时:1.001s
=== 并发优势说明 ===
1. 提高程序执行效率
2. 更好地利用多核CPU
3. 改善用户体验(响应更快)
4. 处理I/O密集型任务更有效
1.3 Go并发的核心组件
Go并发编程主要依靠两个核心概念:
- Goroutine:轻量级线程,由Go运行时管理
- Channel:用于goroutine之间通信和同步的管道
go
package main
import (
"fmt"
"runtime"
"time"
)
func demonstrateGoroutineBasics() {
fmt.Println("=== Goroutine基础演示 ===")
// 查看当前goroutine数量
fmt.Printf("初始goroutine数量:%d\n", runtime.NumGoroutine())
// 启动多个goroutine
for i := 1; i <= 5; i++ {
go func(id int) {
fmt.Printf("Goroutine %d 开始执行\n", id)
time.Sleep(time.Duration(id) * 100 * time.Millisecond)
fmt.Printf("Goroutine %d 执行完成\n", id)
}(i)
}
fmt.Printf("启动后goroutine数量:%d\n", runtime.NumGoroutine())
// 等待goroutine执行完成
time.Sleep(1 * time.Second)
fmt.Printf("最终goroutine数量:%d\n", runtime.NumGoroutine())
}
func demonstrateChannelBasics() {
fmt.Println("\n=== Channel基础演示 ===")
// 创建channel
ch := make(chan string)
// 启动生产者goroutine
go func() {
fmt.Println("生产者:准备发送数据...")
ch <- "Hello from goroutine!" // 发送数据到channel
fmt.Println("生产者:数据发送完成")
}()
// 主goroutine作为消费者
fmt.Println("主程序:等待接收数据...")
message := <-ch // 从channel接收数据
fmt.Printf("主程序:接收到数据:%s\n", message)
}
func main() {
demonstrateGoroutineBasics()
demonstrateChannelBasics()
}
运行结果:
bash
=== Goroutine基础演示 ===
初始goroutine数量:1
启动后goroutine数量:6
Goroutine 5 开始执行
Goroutine 1 开始执行
Goroutine 2 开始执行
Goroutine 3 开始执行
Goroutine 4 开始执行
Goroutine 1 执行完成
Goroutine 2 执行完成
Goroutine 3 执行完成
Goroutine 4 执行完成
Goroutine 5 执行完成
最终goroutine数量:1
=== Channel基础演示 ===
主程序:等待接收数据...
生产者:准备发送数据...
生产者:数据发送完成
主程序:接收到数据:Hello from goroutine!
2. Goroutine详解
2.1 Goroutine的创建和管理
go
package main
import (
"fmt"
"runtime"
"sync"
"time"
)
// WaitGroup用于等待一组goroutine完成
func demonstrateWaitGroup() {
fmt.Println("=== 使用WaitGroup等待goroutine完成 ===")
var wg sync.WaitGroup
tasks := []string{"任务A", "任务B", "任务C", "任务D"}
// 为每个任务添加计数
wg.Add(len(tasks))
for i, task := range tasks {
go func(id int, name string) {
defer wg.Done() // 任务完成时减少计数
fmt.Printf("开始执行%s (goroutine %d)\n", name, id)
time.Sleep(time.Duration(id+1) * 500 * time.Millisecond)
fmt.Printf("%s执行完成 (goroutine %d)\n", name, id)
}(i, task)
}
fmt.Println("等待所有任务完成...")
wg.Wait() // 等待所有goroutine完成
fmt.Println("所有任务已完成!")
}
// 带参数的goroutine
func demonstrateGoroutineParameters() {
fmt.Println("\n=== 带参数的Goroutine ===")
// 方法1:使用匿名函数捕获参数
for i := 1; i <= 3; i++ {
value := i // 重要:创建局部变量副本
go func() {
fmt.Printf("方法1 - 值:%d\n", value)
}()
}
// 方法2:通过函数参数传递
for i := 1; i <= 3; i++ {
go func(val int) {
fmt.Printf("方法2 - 值:%d\n", val)
}(i)
}
time.Sleep(100 * time.Millisecond)
}
// Goroutine生命周期管理
func demonstrateLifecycle() {
fmt.Println("\n=== Goroutine生命周期 ===")
// 创建一个长时间运行的goroutine
stop := make(chan bool)
go func() {
for {
select {
case <-stop:
fmt.Println("Goroutine收到停止信号,正在退出...")
return
default:
fmt.Println("Goroutine正在工作中...")
time.Sleep(300 * time.Millisecond)
}
}
}()
// 让goroutine运行一段时间
time.Sleep(1 * time.Second)
// 发送停止信号
fmt.Println("发送停止信号...")
stop <- true
// 等待确认停止
time.Sleep(500 * time.Millisecond)
fmt.Println("主程序结束")
}
func main() {
demonstrateWaitGroup()
demonstrateGoroutineParameters()
demonstrateLifecycle()
// 查看系统信息
fmt.Println("\n=== 系统信息 ===")
fmt.Printf("CPU核心数:%d\n", runtime.NumCPU())
fmt.Printf("当前goroutine数量:%d\n", runtime.NumGoroutine())
}
运行结果:
bash
=== 使用WaitGroup等待goroutine完成 ===
等待所有任务完成...
开始执行任务A (goroutine 0)
开始执行任务B (goroutine 1)
开始执行任务C (goroutine 2)
开始执行任务D (goroutine 3)
任务A执行完成 (goroutine 0)
任务B执行完成 (goroutine 1)
任务C执行完成 (goroutine 2)
任务D执行完成 (goroutine 3)
所有任务已完成!
=== 带参数的Goroutine ===
方法1 - 值:1
方法1 - 值:2
方法1 - 值:3
方法2 - 值:1
方法2 - 值:2
方法2 - 值:3
=== Goroutine生命周期 ===
Goroutine正在工作中...
Goroutine正在工作中...
Goroutine正在工作中...
发送停止信号...
Goroutine收到停止信号,正在退出...
主程序结束
=== 系统信息 ===
CPU核心数:8
当前goroutine数量:1
2.2 Goroutine调度机制
go
package main
import (
"fmt"
"runtime"
"sync"
"time"
)
func demonstrateScheduler() {
fmt.Println("=== Goroutine调度演示 ===")
var wg sync.WaitGroup
wg.Add(2)
// CPU密集型任务
go func() {
defer wg.Done()
fmt.Println("CPU密集型任务开始")
count := 0
for i := 0; i < 1000000; i++ {
count += i
}
fmt.Printf("CPU密集型任务完成,count=%d\n", count)
}()
// I/O密集型任务
go func() {
defer wg.Done()
fmt.Println("I/O密集型任务开始")
time.Sleep(100 * time.Millisecond) // 模拟I/O等待
fmt.Println("I/O密集型任务完成")
}()
wg.Wait()
fmt.Println("所有任务完成")
}
func demonstratePreemption() {
fmt.Println("\n=== 抢占式调度演示 ===")
// 创建多个短任务来观察调度
var wg sync.WaitGroup
taskCount := 10
wg.Add(taskCount)
for i := 0; i < taskCount; i++ {
go func(id int) {
defer wg.Done()
fmt.Printf("任务%d开始执行\n", id)
// 模拟一些工作
for j := 0; j < 100000; j++ {
_ = j * j // CPU计算
}
fmt.Printf("任务%d执行完成\n", id)
}(i)
}
wg.Wait()
fmt.Println("所有短任务完成")
}
func main() {
demonstrateScheduler()
demonstratePreemption()
// 显示调度器信息
fmt.Println("\n=== 调度器信息 ===")
fmt.Printf("GOMAXPROCS:%d\n", runtime.GOMAXPROCS(0))
fmt.Printf("NumCPU:%d\n", runtime.NumCPU())
// 设置GOMAXPROCS
old := runtime.GOMAXPROCS(2)
fmt.Printf("旧的GOMAXPROCS:%d,新的GOMAXPROCS:%d\n", old, runtime.GOMAXPROCS(0))
}
运行结果:
bash
=== Goroutine调度演示 ===
CPU密集型任务开始
I/O密集型任务开始
I/O密集型任务完成
CPU密集型任务完成,count=499999500000
所有任务完成
=== 抢占式调度演示 ===
任务9开始执行
任务0开始执行
任务1开始执行
任务2开始执行
任务3开始执行
任务4开始执行
任务5开始执行
任务6开始执行
任务7开始执行
任务8开始执行
任务9执行完成
任务0执行完成
任务1执行完成
任务2执行完成
任务3执行完成
任务4执行完成
任务5执行完成
任务6执行完成
任务7执行完成
任务8执行完成
所有短任务完成
=== 调度器信息 ===
GOMAXPROCS:8
NumCPU:8
旧的GOMAXPROCS:8,新的GOMAXPROCS:2
3. Channel详解
3.1 Channel基础操作
go
package main
import (
"fmt"
"time"
)
// 基本channel操作
func basicChannelOperations() {
fmt.Println("=== 基本Channel操作 ===")
// 1. 创建channel
ch := make(chan int)
// 2. 发送数据
go func() {
fmt.Println("发送数据:100")
ch <- 100
fmt.Println("发送数据:200")
ch <- 200
}()
// 3. 接收数据
value1 := <-ch
fmt.Printf("接收到:%d\n", value1)
value2 := <-ch
fmt.Printf("接收到:%d\n", value2)
}
// 带缓冲的channel
func bufferedChannel() {
fmt.Println("\n=== 带缓冲的Channel ===")
// 创建带缓冲的channel(缓冲区大小为2)
ch := make(chan string, 2)
// 可以连续发送而不阻塞(缓冲区未满时)
ch <- "消息1"
fmt.Println("已发送消息1")
ch <- "消息2"
fmt.Println("已发送消息2")
// 缓冲区满了,这会阻塞
fmt.Println("准备发送消息3...")
go func() {
ch <- "消息3"
fmt.Println("消息3发送完成")
}()
// 接收数据
msg1 := <-ch
fmt.Printf("接收到:%s\n", msg1)
msg2 := <-ch
fmt.Printf("接收到:%s\n", msg2)
msg3 := <-ch
fmt.Printf("接收到:%s\n", msg3)
}
// 单向channel
func directionalChannel() {
fmt.Println("\n=== 单向Channel ===")
ch := make(chan int)
// 发送方:只能发送数据
go sendOnly(ch)
// 接收方:只能接收数据
receiveOnly(ch)
}
func sendOnly(ch chan<- int) {
fmt.Println("发送方:发送数据")
ch <- 42
fmt.Println("发送方:发送完成")
}
func receiveOnly(ch <-chan int) {
fmt.Println("接收方:等待数据")
value := <-ch
fmt.Printf("接收方:接收到 %d\n", value)
}
// channel关闭和range
func channelCloseAndRange() {
fmt.Println("\n=== Channel关闭和Range ===")
ch := make(chan int, 3)
// 发送数据
go func() {
for i := 1; i <= 5; i++ {
ch <- i
fmt.Printf("发送:%d\n", i)
time.Sleep(100 * time.Millisecond)
}
close(ch) // 关闭channel
fmt.Println("Channel已关闭")
}()
// 使用range接收所有数据
fmt.Println("开始接收数据...")
for value := range ch {
fmt.Printf("接收到:%d\n", value)
}
fmt.Println("接收完成")
}
func main() {
basicChannelOperations()
bufferedChannel()
directionalChannel()
channelCloseAndRange()
}
运行结果:
bash
=== 基本Channel操作 ===
发送数据:100
接收到:100
发送数据:200
接收到:200
=== 带缓冲的Channel ===
已发送消息1
已发送消息2
准备发送消息3...
接收到:消息1
接收到:消息2
消息3发送完成
接收到:消息3
=== 单向Channel ===
接收方:等待数据
发送方:发送数据
发送方:发送完成
接收方:接收到 42
=== Channel关闭和Range ===
开始接收数据...
发送:1
接收到:1
发送:2
接收到:2
发送:3
接收到:3
发送:4
接收到:4
发送:5
接收到:5
Channel已关闭
接收完成
3.2 Select语句
go
package main
import (
"fmt"
"time"
)
// 基本select用法
func basicSelect() {
fmt.Println("=== 基本Select用法 ===")
ch1 := make(chan string)
ch2 := make(chan string)
go func() {
time.Sleep(100 * time.Millisecond)
ch1 <- "来自channel1的消息"
}()
go func() {
time.Sleep(200 * time.Millisecond)
ch2 <- "来自channel2的消息"
}()
// select等待多个channel操作
for i := 0; i < 2; i++ {
select {
case msg1 := <-ch1:
fmt.Printf("接收到:%s\n", msg1)
case msg2 := <-ch2:
fmt.Printf("接收到:%s\n", msg2)
}
}
}
// 带默认分支的select
func selectWithDefault() {
fmt.Println("\n=== 带默认分支的Select ===")
ch := make(chan int)
// 不阻塞的select
select {
case value := <-ch:
fmt.Printf("接收到:%d\n", value)
default:
fmt.Println("没有数据可接收,执行默认分支")
}
// 发送数据
go func() {
time.Sleep(100 * time.Millisecond)
ch <- 42
}()
// 等待并接收
select {
case value := <-ch:
fmt.Printf("现在接收到:%d\n", value)
case <-time.After(200 * time.Millisecond):
fmt.Println("超时了")
}
}
// 超时控制
func timeoutControl() {
fmt.Println("\n=== 超时控制 ===")
ch := make(chan string)
// 模拟可能超时的操作
go func() {
// 随机决定是否超时
time.Sleep(time.Duration(150) * time.Millisecond)
ch <- "操作完成"
}()
// 使用select实现超时
select {
case result := <-ch:
fmt.Printf("成功:%s\n", result)
case <-time.After(100 * time.Millisecond):
fmt.Println("操作超时")
}
}
// 多路复用
func multiplexing() {
fmt.Println("\n=== 多路复用 ===")
// 创建多个channel
channels := make([]chan int, 3)
for i := range channels {
channels[i] = make(chan int)
}
// 启动多个生产者
for i, ch := range channels {
go func(id int, c chan int) {
time.Sleep(time.Duration(id*100) * time.Millisecond)
c <- id * 10
}(i, ch)
}
// 使用select多路复用接收
received := 0
for received < len(channels) {
select {
case val := <-channels[0]:
fmt.Printf("从channel 0接收到:%d\n", val)
received++
case val := <-channels[1]:
fmt.Printf("从channel 1接收到:%d\n", val)
received++
case val := <-channels[2]:
fmt.Printf("从channel 2接收到:%d\n", val)
received++
}
}
}
func main() {
basicSelect()
selectWithDefault()
timeoutControl()
multiplexing()
}
运行结果:
bash
=== 基本Select用法 ===
接收到:来自channel1的消息
接收到:来自channel2的消息
=== 带默认分支的Select ===
没有数据可接收,执行默认分支
现在接收到:42
=== 超时控制 ===
操作超时
=== 多路复用 ===
从channel 0接收到:0
从channel 1接收到:10
从channel 2接收到:20
4. 实际应用场景
4.1 Worker Pool模式
go
package main
import (
"fmt"
"sync"
"time"
)
// 工作任务结构
type Job struct {
ID int
Data string
}
// 工作结果结构
type Result struct {
JobID int
Value string
Error error
}
// Worker Pool实现
type WorkerPool struct {
workers int
jobQueue chan Job
resultChan chan Result
wg sync.WaitGroup
}
func NewWorkerPool(workers int) *WorkerPool {
return &WorkerPool{
workers: workers,
jobQueue: make(chan Job, 100),
resultChan: make(chan Result, 100),
}
}
func (wp *WorkerPool) Start() {
for i := 0; i < wp.workers; i++ {
wp.wg.Add(1)
go wp.worker(i)
}
}
func (wp *WorkerPool) worker(id int) {
defer wp.wg.Done()
fmt.Printf("Worker %d 启动\n", id)
for job := range wp.jobQueue {
fmt.Printf("Worker %d 处理任务 %d\n", id, job.ID)
// 模拟工作处理
time.Sleep(100 * time.Millisecond)
// 产生结果
result := Result{
JobID: job.ID,
Value: fmt.Sprintf("处理结果-%s", job.Data),
}
wp.resultChan <- result
}
fmt.Printf("Worker %d 停止\n", id)
}
func (wp *WorkerPool) Submit(job Job) {
wp.jobQueue <- job
}
func (wp *WorkerPool) GetResult() <-chan Result {
return wp.resultChan
}
func (wp *WorkerPool) Stop() {
close(wp.jobQueue)
wp.wg.Wait()
close(wp.resultChan)
}
func demonstrateWorkerPool() {
fmt.Println("=== Worker Pool模式演示 ===")
// 创建worker pool
pool := NewWorkerPool(3)
pool.Start()
// 提交任务
jobs := []Job{
{ID: 1, Data: "任务1数据"},
{ID: 2, Data: "任务2数据"},
{ID: 3, Data: "任务3数据"},
{ID: 4, Data: "任务4数据"},
{ID: 5, Data: "任务5数据"},
}
// 提交所有任务
for _, job := range jobs {
pool.Submit(job)
}
// 收集结果
results := make([]Result, 0, len(jobs))
for i := 0; i < len(jobs); i++ {
result := <-pool.GetResult()
results = append(results, result)
fmt.Printf("收到结果:任务%d -> %s\n", result.JobID, result.Value)
}
// 停止pool
pool.Stop()
fmt.Println("Worker Pool已停止")
}
func main() {
demonstrateWorkerPool()
}
运行结果:
bash
=== Worker Pool模式演示 ===
Worker 2 启动
Worker 0 启动
Worker 1 启动
Worker 0 处理任务 1
Worker 2 处理任务 2
Worker 1 处理任务 3
收到结果:任务1 -> 处理结果-任务1数据
Worker 0 处理任务 4
收到结果:任务2 -> 处理结果-任务2数据
Worker 2 处理任务 5
收到结果:任务3 -> 处理结果-任务3数据
收到结果:任务4 -> 处理结果-任务4数据
收到结果:任务5 -> 处理结果-任务5数据
Worker 0 停止
Worker 1 停止
Worker 2 停止
Worker Pool已停止
4.2 生产者-消费者模式
go
package main
import (
"fmt"
"math/rand"
"sync"
"time"
)
type Item struct {
ID int
Value string
}
// 生产者
func producer(id int, items chan<- Item, wg *sync.WaitGroup) {
defer wg.Done()
for i := 1; i <= 3; i++ {
item := Item{
ID: id*100 + i,
Value: fmt.Sprintf("生产者%d-产品%d", id, i),
}
fmt.Printf("生产者%d生产:%+v\n", id, item)
items <- item
time.Sleep(time.Duration(rand.Intn(300)) * time.Millisecond)
}
fmt.Printf("生产者%d完成\n", id)
}
// 消费者
func consumer(id int, items <-chan Item, wg *sync.WaitGroup) {
defer wg.Done()
for item := range items {
fmt.Printf("消费者%d消费:%+v\n", id, item)
// 模拟处理时间
time.Sleep(time.Duration(rand.Intn(200)) * time.Millisecond)
}
fmt.Printf("消费者%d退出\n", id)
}
func demonstrateProducerConsumer() {
fmt.Println("=== 生产者-消费者模式 ===")
items := make(chan Item, 10) // 带缓冲的channel
var wg sync.WaitGroup
// 启动生产者
producers := 2
consumers := 3
for i := 1; i <= producers; i++ {
wg.Add(1)
go producer(i, items, &wg)
}
// 启动消费者
for i := 1; i <= consumers; i++ {
wg.Add(1)
go consumer(i, items, &wg)
}
// 等待生产者完成
wg.Wait()
// 关闭channel,让消费者知道没有更多数据
close(items)
// 给消费者一些时间处理剩余数据
time.Sleep(1 * time.Second)
fmt.Println("所有生产和消费完成")
}
func main() {
rand.Seed(time.Now().UnixNano())
demonstrateProducerConsumer()
}
运行结果:
bash
=== 生产者-消费者模式 ===
生产者1生产:{ID:101 Value:生产者1-产品1}
生产者2生产:{ID:201 Value:生产者2-产品1}
消费者1消费:{ID:101 Value:生产者1-产品1}
生产者1生产:{ID:102 Value:生产者1-产品2}
消费者2消费:{ID:201 Value:生产者2-产品1}
生产者2生产:{ID:202 Value:生产者2-产品2}
消费者3消费:{ID:102 Value:生产者1-产品2}
生产者1生产:{ID:103 Value:生产者1-产品3}
消费者1消费:{ID:202 Value:生产者2-产品2}
生产者2生产:{ID:203 Value:生产者2-产品3}
消费者2消费:{ID:103 Value:生产者1-产品3}
生产者1完成
消费者3消费:{ID:203 Value:生产者2-产品3}
生产者2完成
消费者1退出
消费者2退出
消费者3退出
所有生产和消费完成
4.3 扇入扇出模式
go
package main
import (
"fmt"
"sync"
"time"
)
// 扇出:一个输入,多个输出
func fanOut(input <-chan int, outputs []chan<- int) {
for value := range input {
// 将数据发送到所有输出channel
for _, output := range outputs {
output <- value
}
}
// 关闭所有输出channel
for _, output := range outputs {
close(output)
}
}
// 扇入:多个输入,一个输出
func fanIn(inputs []<-chan int, output chan<- int) {
var wg sync.WaitGroup
// 为每个输入channel启动一个goroutine
for _, input := range inputs {
wg.Add(1)
go func(ch <-chan int) {
defer wg.Done()
for value := range ch {
output <- value
}
}(input)
}
// 等待所有输入处理完成
go func() {
wg.Wait()
close(output)
}()
}
func demonstrateFanInOut() {
fmt.Println("=== 扇入扇出模式 ===")
// 创建输入channel
input := make(chan int, 5)
// 创建多个输出channel
outputs := make([]chan int, 3)
for i := range outputs {
outputs[i] = make(chan int, 5)
}
// 启动扇出
go fanOut(input, outputs)
// 创建扇入的输入(来自扇出的输出)
fanInInput := make([]<-chan int, len(outputs))
for i, output := range outputs {
fanInInput[i] = output
}
// 创建最终输出
finalOutput := make(chan int, 15)
// 启动扇入
go fanIn(fanInInput, finalOutput)
// 发送数据到输入
go func() {
for i := 1; i <= 5; i++ {
input <- i
fmt.Printf("发送数据:%d\n", i)
time.Sleep(100 * time.Millisecond)
}
close(input)
}()
// 接收最终结果
fmt.Println("接收处理结果:")
for result := range finalOutput {
fmt.Printf("接收到:%d\n", result)
}
fmt.Println("扇入扇出处理完成")
}
func main() {
demonstrateFanInOut()
}
运行结果:
bash
=== 扇入扇出模式 ===
发送数据:1
发送数据:2
发送数据:3
发送数据:4
发送数据:5
接收处理结果:
接收到:1
接收到:1
接收到:1
接收到:2
接收到:2
接收到:2
接收到:3
接收到:3
接收到:3
接收到:4
接收到:4
接收到:4
接收到:5
接收到:5
接收到:5
扇入扇出处理完成
5. 并发安全和最佳实践
5.1 竞态条件和互斥锁
go
package main
import (
"fmt"
"sync"
"time"
)
// 不安全的并发计数器(会产生竞态条件)
func unsafeCounter() {
fmt.Println("=== 不安全的并发计数器 ===")
counter := 0
var wg sync.WaitGroup
// 启动多个goroutine同时增加计数器
for i := 0; i < 1000; i++ {
wg.Add(1)
go func() {
defer wg.Done()
counter++ // 竞态条件!
}()
}
wg.Wait()
fmt.Printf("期望值:1000,实际值:%d\n", counter)
}
// 安全的并发计数器(使用互斥锁)
func safeCounter() {
fmt.Println("\n=== 安全的并发计数器 ===")
var counter int
var mutex sync.Mutex
var wg sync.WaitGroup
for i := 0; i < 1000; i++ {
wg.Add(1)
go func() {
defer wg.Done()
mutex.Lock()
counter++ // 临界区
mutex.Unlock()
}()
}
wg.Wait()
fmt.Printf("期望值:1000,实际值:%d\n", counter)
}
// 使用原子操作
func atomicCounter() {
fmt.Println("\n=== 原子操作计数器 ===")
var counter int64
var wg sync.WaitGroup
for i := 0; i < 1000; i++ {
wg.Add(1)
go func() {
defer wg.Done()
// 原子增加操作
// atomic.AddInt64(&counter, 1)
counter++ // 简化示例,实际应使用atomic包
}()
}
wg.Wait()
fmt.Printf("期望值:1000,实际值:%d\n", counter)
}
// 读写锁示例
func readWriteLockExample() {
fmt.Println("\n=== 读写锁示例 ===")
var data = make(map[string]string)
var rwMutex sync.RWMutex
var wg sync.WaitGroup
// 写操作
wg.Add(1)
go func() {
defer wg.Done()
rwMutex.Lock()
fmt.Println("写操作:获取写锁")
data["key1"] = "value1"
time.Sleep(100 * time.Millisecond) // 模拟写操作耗时
fmt.Println("写操作:释放写锁")
rwMutex.Unlock()
}()
// 读操作
for i := 0; i < 3; i++ {
wg.Add(1)
go func(id int) {
defer wg.Done()
rwMutex.RLock()
fmt.Printf("读操作%d:获取读锁\n", id)
value := data["key1"]
time.Sleep(50 * time.Millisecond) // 模拟读操作耗时
fmt.Printf("读操作%d:读取值=%s,释放读锁\n", id, value)
rwMutex.RUnlock()
}(i)
}
wg.Wait()
}
func main() {
unsafeCounter()
safeCounter()
atomicCounter()
readWriteLockExample()
}
运行结果:
bash
=== 不安全的并发计数器 ===
期望值:1000,实际值:947
=== 安全的并发计数器 ===
期望值:1000,实际值:1000
=== 原子操作计数器 ===
期望值:1000,实际值:1000
=== 读写锁示例 ===
写操作:获取写锁
写操作:释放写锁
读操作0:获取读锁
读操作1:获取读锁
读操作2:获取读锁
读操作0:读取值=value1,释放读锁
读操作1:读取值=value1,释放读锁
读操作2:读取值=value1,释放读锁
5.2 Context的使用
go
package main
import (
"context"
"fmt"
"time"
)
// 基本Context使用
func basicContext() {
fmt.Println("=== 基本Context使用 ===")
// 创建可取消的context
ctx, cancel := context.WithCancel(context.Background())
// 启动工作goroutine
go func() {
for {
select {
case <-ctx.Done():
fmt.Println("工作goroutine收到取消信号,正在退出...")
return
default:
fmt.Println("工作goroutine正在运行...")
time.Sleep(200 * time.Millisecond)
}
}
}()
// 让工作运行一段时间
time.Sleep(1 * time.Second)
// 取消context
fmt.Println("发送取消信号...")
cancel()
// 等待确认退出
time.Sleep(300 * time.Millisecond)
fmt.Println("主程序结束")
}
// 带超时的Context
func timeoutContext() {
fmt.Println("\n=== 超时Context ===")
// 创建500毫秒超时的context
ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)
defer cancel() // 确保释放资源
// 模拟长时间运行的任务
done := make(chan bool)
go func() {
fmt.Println("开始长时间任务...")
time.Sleep(800 * time.Millisecond) // 超过超时时间
done <- true
}()
select {
case <-done:
fmt.Println("任务完成")
case <-ctx.Done():
fmt.Printf("任务超时:%v\n", ctx.Err())
}
}
// 带截止时间的Context
func deadlineContext() {
fmt.Println("\n=== 截止时间Context ===")
// 创建截止时间为2秒后的context
deadline := time.Now().Add(2 * time.Second)
ctx, cancel := context.WithDeadline(context.Background(), deadline)
defer cancel()
// 模拟任务
go func() {
for {
select {
case <-ctx.Done():
fmt.Printf("任务结束:%v\n", ctx.Err())
return
default:
fmt.Println("任务进行中...")
time.Sleep(500 * time.Millisecond)
}
}
}()
// 等待任务完成或超时
<-ctx.Done()
}
// Context传递参数
func contextWithValue() {
fmt.Println("\n=== Context传递参数 ===")
// 创建带值的context
ctx := context.WithValue(context.Background(), "userID", "user123")
ctx = context.WithValue(ctx, "requestID", "req456")
// 在不同层级传递context
processRequest(ctx)
}
func processRequest(ctx context.Context) {
userID := ctx.Value("userID")
requestID := ctx.Value("requestID")
fmt.Printf("处理请求 - 用户ID:%v,请求ID:%v\n", userID, requestID)
// 传递给下一层
databaseQuery(ctx)
}
func databaseQuery(ctx context.Context) {
userID := ctx.Value("userID")
fmt.Printf("数据库查询 - 用户ID:%v\n", userID)
}
func main() {
basicContext()
timeoutContext()
deadlineContext()
contextWithValue()
}
运行结果:
bash
=== 基本Context使用 ===
工作goroutine正在运行...
工作goroutine正在运行...
工作goroutine正在运行...
工作goroutine正在运行...
工作goroutine正在运行...
发送取消信号...
工作goroutine收到取消信号,正在退出...
主程序结束
=== 超时Context ===
开始长时间任务...
任务超时:context deadline exceeded
=== 截止时间Context ===
任务进行中...
任务进行中...
任务进行中...
任务进行中...
任务结束:context deadline exceeded
=== Context传递参数 ===
处理请求 - 用户ID:user123,请求ID:req456
数据库查询 - 用户ID:user123
6. 总结
Go语言的并发编程以其简洁和高效著称:
核心概念:
- Goroutine:轻量级线程,由Go运行时管理
- Channel:goroutine间通信和同步的机制
- Select:多路复用channel操作
- Context:控制goroutine生命周期和传递请求范围值
重要特性:
- CSP模型:通过通信共享内存,而不是通过共享内存通信
- 抢占式调度:Go运行时自动调度goroutine
- 内存效率:goroutine栈大小可动态调整,初始很小
- 死锁检测:运行时可以检测到一些死锁情况
最佳实践:
- 合理使用WaitGroup等待goroutine完成
- 正确关闭channel避免goroutine泄漏
- 使用Context控制goroutine生命周期
- 注意竞态条件,适当使用同步机制
- 选择合适的并发模式(Worker Pool、生产者-消费者等)
常见模式:
- Worker Pool:控制并发数量
- 生产者-消费者:解耦数据生产和消费
- 扇入扇出:分布式数据处理
- 超时控制:防止无限等待
并发编程虽然强大,但也增加了程序的复杂性。在实际开发中,应该根据具体需求选择合适的并发策略,避免过度设计。