This commit is contained in:
2025-08-24 11:24:52 +08:00
parent ec23aa5c26
commit 61e51ad014
12 changed files with 11554 additions and 4 deletions

View File

@@ -0,0 +1,738 @@
/*
01-goroutines.go - Go 语言 Goroutines 详解
学习目标:
1. 理解 goroutine 的概念和特性
2. 掌握 goroutine 的创建和使用
3. 学会 goroutine 的同步和通信
4. 了解 goroutine 的调度机制
5. 掌握 goroutine 的最佳实践
知识点:
- goroutine 的基本概念
- go 关键字的使用
- goroutine 与线程的区别
- goroutine 的生命周期
- goroutine 泄漏的预防
- 并发安全的考虑
*/
package main
import (
"fmt"
"runtime"
"sync"
"time"
)
func main() {
fmt.Println("=== Go 语言 Goroutines 详解 ===\n")
// 演示基本的 goroutine
demonstrateBasicGoroutines()
// 演示 goroutine 的并发执行
demonstrateConcurrentExecution()
// 演示 goroutine 的同步
demonstrateGoroutineSynchronization()
// 演示 goroutine 的通信
demonstrateGoroutineCommunication()
// 演示 goroutine 池
demonstrateGoroutinePool()
// 演示 goroutine 的生命周期管理
demonstrateGoroutineLifecycle()
// 演示 goroutine 的最佳实践
demonstrateBestPractices()
}
// demonstrateBasicGoroutines 演示基本的 goroutine
func demonstrateBasicGoroutines() {
fmt.Println("1. 基本的 Goroutine:")
// goroutine 的基本概念
fmt.Printf(" Goroutine 的基本概念:\n")
fmt.Printf(" - 轻量级线程,由 Go 运行时管理\n")
fmt.Printf(" - 使用 go 关键字启动\n")
fmt.Printf(" - 栈大小动态增长,初始只有 2KB\n")
fmt.Printf(" - 由 Go 调度器调度,不是操作系统线程\n")
fmt.Printf(" - 可以创建数百万个 goroutine\n")
// 基本 goroutine 示例
fmt.Printf(" 基本 goroutine 示例:\n")
// 普通函数调用
fmt.Printf(" 普通函数调用:\n")
sayHello("World")
sayHello("Go")
// 使用 goroutine
fmt.Printf(" 使用 goroutine:\n")
go sayHello("Goroutine 1")
go sayHello("Goroutine 2")
go sayHello("Goroutine 3")
// 等待 goroutine 完成
time.Sleep(100 * time.Millisecond)
// 匿名函数 goroutine
fmt.Printf(" 匿名函数 goroutine:\n")
for i := 1; i <= 3; i++ {
go func(id int) {
fmt.Printf(" 匿名 goroutine %d 执行\n", id)
}(i)
}
time.Sleep(100 * time.Millisecond)
// 闭包 goroutine
fmt.Printf(" 闭包 goroutine:\n")
message := "Hello from closure"
go func() {
fmt.Printf(" %s\n", message)
}()
time.Sleep(100 * time.Millisecond)
fmt.Println()
}
// demonstrateConcurrentExecution 演示 goroutine 的并发执行
func demonstrateConcurrentExecution() {
fmt.Println("2. Goroutine 的并发执行:")
// 并发执行任务
fmt.Printf(" 并发执行任务:\n")
start := time.Now()
// 串行执行
fmt.Printf(" 串行执行:\n")
serialStart := time.Now()
task("任务1", 200*time.Millisecond)
task("任务2", 200*time.Millisecond)
task("任务3", 200*time.Millisecond)
serialDuration := time.Since(serialStart)
fmt.Printf(" 串行执行耗时: %v\n", serialDuration)
// 并发执行
fmt.Printf(" 并发执行:\n")
concurrentStart := time.Now()
var wg sync.WaitGroup
wg.Add(3)
go func() {
defer wg.Done()
task("并发任务1", 200*time.Millisecond)
}()
go func() {
defer wg.Done()
task("并发任务2", 200*time.Millisecond)
}()
go func() {
defer wg.Done()
task("并发任务3", 200*time.Millisecond)
}()
wg.Wait()
concurrentDuration := time.Since(concurrentStart)
fmt.Printf(" 并发执行耗时: %v\n", concurrentDuration)
// CPU 密集型任务
fmt.Printf(" CPU 密集型任务:\n")
numCPU := runtime.NumCPU()
fmt.Printf(" CPU 核心数: %d\n", numCPU)
// 设置使用的 CPU 核心数
runtime.GOMAXPROCS(numCPU)
cpuStart := time.Now()
var cpuWg sync.WaitGroup
for i := 0; i < numCPU; i++ {
cpuWg.Add(1)
go func(id int) {
defer cpuWg.Done()
result := fibonacci(35)
fmt.Printf(" Goroutine %d: fibonacci(35) = %d\n", id, result)
}(i)
}
cpuWg.Wait()
cpuDuration := time.Since(cpuStart)
fmt.Printf(" CPU 密集型任务耗时: %v\n", cpuDuration)
totalDuration := time.Since(start)
fmt.Printf(" 总耗时: %v\n", totalDuration)
fmt.Println()
}
// demonstrateGoroutineSynchronization 演示 goroutine 的同步
func demonstrateGoroutineSynchronization() {
fmt.Println("3. Goroutine 的同步:")
// 使用 WaitGroup 同步
fmt.Printf(" 使用 WaitGroup 同步:\n")
var wg sync.WaitGroup
for i := 1; i <= 5; i++ {
wg.Add(1)
go func(id int) {
defer wg.Done()
fmt.Printf(" Worker %d 开始工作\n", id)
time.Sleep(time.Duration(id*100) * time.Millisecond)
fmt.Printf(" Worker %d 完成工作\n", id)
}(i)
}
fmt.Printf(" 等待所有 worker 完成...\n")
wg.Wait()
fmt.Printf(" 所有 worker 已完成\n")
// 使用 Mutex 保护共享资源
fmt.Printf(" 使用 Mutex 保护共享资源:\n")
var counter Counter
var counterWg sync.WaitGroup
// 启动多个 goroutine 增加计数器
for i := 0; i < 10; i++ {
counterWg.Add(1)
go func(id int) {
defer counterWg.Done()
for j := 0; j < 1000; j++ {
counter.Increment()
}
fmt.Printf(" Goroutine %d 完成\n", id)
}(i)
}
counterWg.Wait()
fmt.Printf(" 最终计数器值: %d\n", counter.Value())
// 使用 Once 确保只执行一次
fmt.Printf(" 使用 Once 确保只执行一次:\n")
var once sync.Once
var onceWg sync.WaitGroup
for i := 0; i < 5; i++ {
onceWg.Add(1)
go func(id int) {
defer onceWg.Done()
once.Do(func() {
fmt.Printf(" 初始化操作只执行一次 (来自 goroutine %d)\n", id)
})
fmt.Printf(" Goroutine %d 执行完毕\n", id)
}(i)
}
onceWg.Wait()
fmt.Println()
}
// demonstrateGoroutineCommunication 演示 goroutine 的通信
func demonstrateGoroutineCommunication() {
fmt.Println("4. Goroutine 的通信:")
// 使用 channel 通信
fmt.Printf(" 使用 channel 通信:\n")
// 简单的 channel 通信
ch := make(chan string)
go func() {
time.Sleep(100 * time.Millisecond)
ch <- "Hello from goroutine"
}()
message := <-ch
fmt.Printf(" 接收到消息: %s\n", message)
// 生产者-消费者模式
fmt.Printf(" 生产者-消费者模式:\n")
jobs := make(chan int, 5)
results := make(chan int, 5)
// 启动 3 个 worker
for w := 1; w <= 3; w++ {
go worker(w, jobs, results)
}
// 发送 5 个任务
for j := 1; j <= 5; j++ {
jobs <- j
}
close(jobs)
// 收集结果
for r := 1; r <= 5; r++ {
result := <-results
fmt.Printf(" 结果: %d\n", result)
}
// 扇出/扇入模式
fmt.Printf(" 扇出/扇入模式:\n")
input := make(chan int)
output1 := make(chan int)
output2 := make(chan int)
// 扇出:一个输入分发到多个处理器
go func() {
for i := 1; i <= 10; i++ {
input <- i
}
close(input)
}()
// 处理器 1
go func() {
for num := range input {
output1 <- num * 2
}
close(output1)
}()
// 处理器 2
go func() {
for num := range input {
output2 <- num * 3
}
close(output2)
}()
// 扇入:合并多个输出
merged := fanIn(output1, output2)
fmt.Printf(" 合并结果: ")
for result := range merged {
fmt.Printf("%d ", result)
}
fmt.Printf("\n")
fmt.Println()
}
// demonstrateGoroutinePool 演示 goroutine 池
func demonstrateGoroutinePool() {
fmt.Println("5. Goroutine 池:")
// 固定大小的 goroutine 池
fmt.Printf(" 固定大小的 goroutine 池:\n")
const numWorkers = 3
const numJobs = 10
jobs := make(chan Job, numJobs)
results := make(chan Result, numJobs)
// 启动 worker 池
for w := 1; w <= numWorkers; w++ {
go jobWorker(w, jobs, results)
}
// 发送任务
for j := 1; j <= numJobs; j++ {
jobs <- Job{ID: j, Data: fmt.Sprintf("任务数据 %d", j)}
}
close(jobs)
// 收集结果
for r := 1; r <= numJobs; r++ {
result := <-results
fmt.Printf(" %s\n", result.Message)
}
// 动态 goroutine 池
fmt.Printf(" 动态 goroutine 池:\n")
pool := NewWorkerPool(5, 20)
pool.Start()
// 提交任务
for i := 1; i <= 15; i++ {
taskID := i
pool.Submit(func() {
fmt.Printf(" 执行任务 %d\n", taskID)
time.Sleep(100 * time.Millisecond)
})
}
pool.Stop()
fmt.Println()
}
// demonstrateGoroutineLifecycle 演示 goroutine 的生命周期管理
func demonstrateGoroutineLifecycle() {
fmt.Println("6. Goroutine 的生命周期管理:")
// 优雅关闭
fmt.Printf(" 优雅关闭:\n")
done := make(chan bool)
quit := make(chan bool)
go func() {
for {
select {
case <-quit:
fmt.Printf(" 接收到退出信号,正在清理...\n")
time.Sleep(100 * time.Millisecond)
fmt.Printf(" 清理完成\n")
done <- true
return
default:
fmt.Printf(" 工作中...\n")
time.Sleep(200 * time.Millisecond)
}
}
}()
time.Sleep(500 * time.Millisecond)
fmt.Printf(" 发送退出信号\n")
quit <- true
<-done
fmt.Printf(" Goroutine 已优雅退出\n")
// 超时控制
fmt.Printf(" 超时控制:\n")
timeout := time.After(300 * time.Millisecond)
finished := make(chan bool)
go func() {
time.Sleep(500 * time.Millisecond) // 模拟长时间运行的任务
finished <- true
}()
select {
case <-finished:
fmt.Printf(" 任务完成\n")
case <-timeout:
fmt.Printf(" 任务超时\n")
}
// 错误处理
fmt.Printf(" 错误处理:\n")
errorCh := make(chan error, 1)
go func() {
defer func() {
if r := recover(); r != nil {
errorCh <- fmt.Errorf("goroutine panic: %v", r)
}
}()
// 模拟可能 panic 的操作
if time.Now().UnixNano()%2 == 0 {
panic("模拟 panic")
}
fmt.Printf(" 任务正常完成\n")
errorCh <- nil
}()
if err := <-errorCh; err != nil {
fmt.Printf(" 捕获到错误: %v\n", err)
}
fmt.Println()
}
// demonstrateBestPractices 演示 goroutine 的最佳实践
func demonstrateBestPractices() {
fmt.Println("7. Goroutine 的最佳实践:")
fmt.Printf(" 最佳实践原则:\n")
fmt.Printf(" 1. 避免 goroutine 泄漏\n")
fmt.Printf(" 2. 使用 context 进行取消和超时控制\n")
fmt.Printf(" 3. 合理控制 goroutine 数量\n")
fmt.Printf(" 4. 使用 channel 进行通信而不是共享内存\n")
fmt.Printf(" 5. 正确处理 panic 和错误\n")
// 避免 goroutine 泄漏
fmt.Printf(" 避免 goroutine 泄漏:\n")
// 错误示例:可能导致 goroutine 泄漏
fmt.Printf(" 错误示例 - 可能导致泄漏:\n")
leakyCh := make(chan int)
go func() {
// 这个 goroutine 可能永远阻塞
leakyCh <- 42
}()
// 如果不读取 channelgoroutine 会泄漏
// 正确示例:使用缓冲 channel 或确保读取
fmt.Printf(" 正确示例 - 避免泄漏:\n")
safeCh := make(chan int, 1) // 缓冲 channel
go func() {
safeCh <- 42
fmt.Printf(" 安全的 goroutine 完成\n")
}()
<-safeCh
// 使用 defer 清理资源
fmt.Printf(" 使用 defer 清理资源:\n")
var cleanupWg sync.WaitGroup
cleanupWg.Add(1)
go func() {
defer cleanupWg.Done()
defer fmt.Printf(" 资源已清理\n")
fmt.Printf(" 执行任务...\n")
time.Sleep(100 * time.Millisecond)
}()
cleanupWg.Wait()
// 监控 goroutine 数量
fmt.Printf(" 监控 goroutine 数量:\n")
fmt.Printf(" 当前 goroutine 数量: %d\n", runtime.NumGoroutine())
// 启动一些 goroutine
var monitorWg sync.WaitGroup
for i := 0; i < 5; i++ {
monitorWg.Add(1)
go func(id int) {
defer monitorWg.Done()
time.Sleep(100 * time.Millisecond)
}(i)
}
fmt.Printf(" 启动 5 个 goroutine 后: %d\n", runtime.NumGoroutine())
monitorWg.Wait()
fmt.Printf(" goroutine 完成后: %d\n", runtime.NumGoroutine())
fmt.Println()
}
// ========== 辅助函数和类型定义 ==========
// sayHello 简单的问候函数
func sayHello(name string) {
fmt.Printf(" Hello, %s!\n", name)
}
// task 模拟一个耗时任务
func task(name string, duration time.Duration) {
fmt.Printf(" %s 开始执行\n", name)
time.Sleep(duration)
fmt.Printf(" %s 执行完成\n", name)
}
// fibonacci 计算斐波那契数列CPU 密集型任务)
func fibonacci(n int) int {
if n <= 1 {
return n
}
return fibonacci(n-1) + fibonacci(n-2)
}
// Counter 线程安全的计数器
type Counter struct {
mu sync.Mutex
value int
}
func (c *Counter) Increment() {
c.mu.Lock()
defer c.mu.Unlock()
c.value++
}
func (c *Counter) Value() int {
c.mu.Lock()
defer c.mu.Unlock()
return c.value
}
// worker 工作函数
func worker(id int, jobs <-chan int, results chan<- int) {
for j := range jobs {
fmt.Printf(" Worker %d 开始处理任务 %d\n", id, j)
time.Sleep(100 * time.Millisecond)
results <- j * 2
}
}
// fanIn 扇入函数,合并多个 channel
func fanIn(input1, input2 <-chan int) <-chan int {
output := make(chan int)
go func() {
defer close(output)
for input1 != nil || input2 != nil {
select {
case val, ok := <-input1:
if !ok {
input1 = nil
} else {
output <- val
}
case val, ok := <-input2:
if !ok {
input2 = nil
} else {
output <- val
}
}
}
}()
return output
}
// Job 任务结构
type Job struct {
ID int
Data string
}
// Result 结果结构
type Result struct {
JobID int
Message string
}
// jobWorker 任务处理器
func jobWorker(id int, jobs <-chan Job, results chan<- Result) {
for job := range jobs {
fmt.Printf(" Worker %d 处理任务 %d\n", id, job.ID)
time.Sleep(100 * time.Millisecond)
results <- Result{
JobID: job.ID,
Message: fmt.Sprintf("Worker %d 完成任务 %d: %s", id, job.ID, job.Data),
}
}
}
// WorkerPool 动态工作池
type WorkerPool struct {
maxWorkers int
taskQueue chan func()
quit chan bool
wg sync.WaitGroup
}
// NewWorkerPool 创建新的工作池
func NewWorkerPool(maxWorkers, queueSize int) *WorkerPool {
return &WorkerPool{
maxWorkers: maxWorkers,
taskQueue: make(chan func(), queueSize),
quit: make(chan bool),
}
}
// Start 启动工作池
func (p *WorkerPool) Start() {
for i := 0; i < p.maxWorkers; i++ {
p.wg.Add(1)
go p.worker(i + 1)
}
}
// Submit 提交任务
func (p *WorkerPool) Submit(task func()) {
select {
case p.taskQueue <- task:
default:
fmt.Printf(" 任务队列已满,任务被丢弃\n")
}
}
// Stop 停止工作池
func (p *WorkerPool) Stop() {
close(p.taskQueue)
p.wg.Wait()
}
// worker 工作池的工作函数
func (p *WorkerPool) worker(id int) {
defer p.wg.Done()
for task := range p.taskQueue {
fmt.Printf(" Pool Worker %d 执行任务\n", id)
task()
}
fmt.Printf(" Pool Worker %d 退出\n", id)
}
/*
运行这个程序:
go run 01-goroutines.go
学习要点:
1. Goroutine 是 Go 语言的轻量级线程
2. 使用 go 关键字启动 goroutine
3. Goroutine 之间需要同步和通信机制
4. 使用 WaitGroup、Mutex、Once 等<><E7AD89><EFBFBD>步原语
5. 通过 channel 进行 goroutine 间通信
Goroutine 的特性:
1. 轻量级:初始栈大小只有 2KB
2. 动态增长:栈大小可以动态调整
3. 多路复用:多个 goroutine 可以在少数 OS 线程上运行
4. 协作式调度:由 Go 运行时调度
5. 高效通信:通过 channel 进行通信
同步机制:
1. WaitGroup等待一组 goroutine 完成
2. Mutex互斥锁保护共享资源
3. RWMutex读写锁允许多个读者
4. Once确保函数只执行一次
5. Cond条件变量用于等待条件
通信模式:
1. 简单通信:通过 channel 发送和接收数据
2. 生产者-消费者:使用缓冲 channel
3. 扇出/扇入:一对多和多对一的通信
4. 管道:链式处理数据
5. 工作池:固定数量的 worker 处理任务
最佳实践:
1. 避免 goroutine 泄漏
2. 使用 context 进行取消和超时控制
3. 合理控制 goroutine 数量
4. 使用 channel 进行通信而不是共享内存
5. 正确处理 panic 和错误
6. 使用 defer 清理资源
7. 监控 goroutine 数量
常见陷阱:
1. 忘记等待 goroutine 完成
2. 在循环中使用闭包时的变量捕获问题
3. 无缓冲 channel 导致的死锁
4. goroutine 泄漏
5. 竞态条件
性能考虑:
1. Goroutine 创建成本很低
2. 上下文切换开销小
3. 内存使用效率高
4. 适合 I/O 密集型任务
5. CPU 密集型任务需要考虑 GOMAXPROCS
注意事项:
1. main 函数退出时所有 goroutine 都会终止
2. goroutine 中的 panic 会导致整个程序崩溃
3. 共享变量需要同步保护
4. channel 的关闭只能由发送方执行
5. 避免在 goroutine 中使用全局变量
*/

View File

@@ -0,0 +1,840 @@
/*
02-channels.go - Go 语言 Channels 详解
学习目标:
1. 理解 channel 的概念和特性
2. 掌握 channel 的创建和使用
3. 学会不同类型的 channel
4. 了解 channel 的方向性
5. 掌握 channel 的关闭和检测
知识点:
- channel 的基本概念
- 无缓冲和有缓冲 channel
- channel 的方向性
- channel 的关闭
- range 和 select 与 channel
- channel 的常见模式
*/
package main
import (
"fmt"
"math/rand"
"sync"
"time"
)
func main() {
fmt.Println("=== Go 语言 Channels 详解 ===\n")
// 演示基本的 channel
demonstrateBasicChannels()
// 演示缓冲 channel
demonstrateBufferedChannels()
// 演示 channel 的方向性
demonstrateChannelDirections()
// 演示 channel 的关闭
demonstrateChannelClosing()
// 演示 channel 与 range
demonstrateChannelRange()
// 演示 channel 的常见模式
demonstrateChannelPatterns()
// 演示 channel 的高级用法
demonstrateAdvancedChannelUsage()
}
// demonstrateBasicChannels 演示基本的 channel
func demonstrateBasicChannels() {
fmt.Println("1. 基本的 Channel:")
// channel 的基本概念
fmt.Printf(" Channel 的基本概念:\n")
fmt.Printf(" - Go 语言的核心特性,用于 goroutine 间通信\n")
fmt.Printf(" - 类型安全的管道,可以传递特定类型的数据\n")
fmt.Printf(" - 默认是无缓冲的,发送和接收会阻塞\n")
fmt.Printf(" - 遵循 'Don't communicate by sharing memory; share memory by communicating'\n")
// 创建和使用 channel
fmt.Printf(" 创建和使用 channel:\n")
// 创建一个 int 类型的 channel
ch := make(chan int)
// 在 goroutine 中发送数据
go func() {
fmt.Printf(" 发送数据到 channel\n")
ch <- 42
}()
// 从 channel 接收数据
value := <-ch
fmt.Printf(" 从 channel 接收到: %d\n", value)
// 双向通信
fmt.Printf(" 双向通信:\n")
messages := make(chan string)
responses := make(chan string)
// 启动一个 echo 服务
go echoServer(messages, responses)
// 发送消息并接收响应
messages <- "Hello"
response := <-responses
fmt.Printf(" 发送: Hello, 接收: %s\n", response)
messages <- "World"
response = <-responses
fmt.Printf(" 发送: World, 接收: %s\n", response)
// 关闭 channels
close(messages)
close(responses)
// 同步使用 channel
fmt.Printf(" 同步使用 channel:\n")
done := make(chan bool)
go func() {
fmt.Printf(" 执行异步任务...\n")
time.Sleep(200 * time.Millisecond)
fmt.Printf(" 异步任务完成\n")
done <- true
}()
fmt.Printf(" 等待异步任务完成...\n")
<-done
fmt.Printf(" 主程序继续执行\n")
fmt.Println()
}
// demonstrateBufferedChannels 演示缓冲 channel
func demonstrateBufferedChannels() {
fmt.Println("2. 缓冲 Channel:")
// 无缓冲 vs 有缓冲
fmt.Printf(" 无缓冲 vs 有缓冲:\n")
// 无缓冲 channel同步
fmt.Printf(" 无缓冲 channel (同步):\n")
unbuffered := make(chan string)
go func() {
fmt.Printf(" 发送到无缓冲 channel\n")
unbuffered <- "sync message"
fmt.Printf(" 无缓冲 channel 发送完成\n")
}()
time.Sleep(100 * time.Millisecond) // 让 goroutine 先运行
msg := <-unbuffered
fmt.Printf(" 接收到: %s\n", msg)
// 有缓冲 channel异步
fmt.Printf(" 有缓冲 channel (异步):\n")
buffered := make(chan string, 2)
fmt.Printf(" 发送到有缓冲 channel\n")
buffered <- "async message 1"
buffered <- "async message 2"
fmt.Printf(" 有缓冲 channel 发送完成(未阻塞)\n")
fmt.Printf(" 接收到: %s\n", <-buffered)
fmt.Printf(" 接收到: %s\n", <-buffered)
// 缓冲区满时的行为
fmt.Printf(" 缓冲区满时的行为:\n")
fullBuffer := make(chan int, 2)
// 填满缓冲区
fullBuffer <- 1
fullBuffer <- 2
fmt.Printf(" 缓冲区已满 (2/2)\n")
// 尝试再发送一个值(会阻塞)
go func() {
fmt.Printf(" 尝试发送第三个值(会阻塞)\n")
fullBuffer <- 3
fmt.Printf(" 第三个值发送成功\n")
}()
time.Sleep(100 * time.Millisecond)
fmt.Printf(" 接收一个值: %d\n", <-fullBuffer)
time.Sleep(100 * time.Millisecond)
fmt.Printf(" 接收一个值: %d\n", <-fullBuffer)
fmt.Printf(" 接收一个值: %d\n", <-fullBuffer)
// 生产者-消费者模式
fmt.Printf(" 生产者-消费者模式:\n")
const bufferSize = 5
const numProducers = 2
const numConsumers = 3
const numItems = 10
items := make(chan int, bufferSize)
var wg sync.WaitGroup
// 启动生产者
for i := 0; i < numProducers; i++ {
wg.Add(1)
go producer(i+1, items, numItems/numProducers, &wg)
}
// 启动消费者
for i := 0; i < numConsumers; i++ {
wg.Add(1)
go consumer(i+1, items, &wg)
}
// 等待所有生产者完成,然后关闭 channel
go func() {
wg.Wait()
close(items)
}()
// 等待一段时间让消费者处理完所有项目
time.Sleep(500 * time.Millisecond)
fmt.Println()
}
// demonstrateChannelDirections 演示 channel 的方向性
func demonstrateChannelDirections() {
fmt.Println("3. Channel 的方向性:")
// 双向 channel
fmt.Printf(" 双向 channel:\n")
bidirectional := make(chan string)
go func() {
bidirectional <- "双向消息"
}()
message := <-bidirectional
fmt.Printf(" 接收到: %s\n", message)
// 只发送 channel
fmt.Printf(" 只发送 channel:\n")
sendOnly := make(chan int)
go sender(sendOnly)
value := <-sendOnly
fmt.Printf(" 从只发送 channel 接收到: %d\n", value)
// 只接收 channel
fmt.Printf(" 只接收 channel:\n")
receiveOnly := make(chan int, 1)
receiveOnly <- 100
go receiver(receiveOnly)
time.Sleep(100 * time.Millisecond)
// 管道模式
fmt.Printf(" 管道模式:\n")
numbers := make(chan int)
squares := make(chan int)
cubes := make(chan int)
// 启动管道
go generateNumbers(numbers)
go squareNumbers(numbers, squares)
go cubeNumbers(squares, cubes)
// 读取最终结果
for i := 0; i < 5; i++ {
result := <-cubes
fmt.Printf(" 管道结果: %d\n", result)
}
fmt.Println()
}
// demonstrateChannelClosing 演示 channel 的关闭
func demonstrateChannelClosing() {
fmt.Println("4. Channel 的关闭:")
// 基本的 channel 关闭
fmt.Printf(" 基本的 channel 关闭:\n")
ch := make(chan int, 3)
// 发送一些值
ch <- 1
ch <- 2
ch <- 3
// 关闭 channel
close(ch)
// 从已关闭的 channel 读取
for i := 0; i < 4; i++ {
value, ok := <-ch
if ok {
fmt.Printf(" 接收到值: %d\n", value)
} else {
fmt.Printf(" channel 已关闭,接收到零值: %d\n", value)
}
}
// 检测 channel 是否关闭
fmt.Printf(" 检测 channel 是否关闭:\n")
status := make(chan string, 2)
status <- "active"
status <- "inactive"
close(status)
for {
value, ok := <-status
if !ok {
fmt.Printf(" channel 已关闭,退出循环\n")
break
}
fmt.Printf(" 状态: %s\n", value)
}
// 多个发送者的关闭模式
fmt.Printf(" 多个发送者的关闭模式:\n")
data := make(chan int)
done := make(chan bool)
// 启动多个发送者
for i := 1; i <= 3; i++ {
go multipleSender(i, data, done)
}
// 接收数据
go func() {
for value := range data {
fmt.Printf(" 接收到: %d\n", value)
}
fmt.Printf(" 数据接收完成\n")
}()
// 等待一段时间后通知所有发送者停止
time.Sleep(300 * time.Millisecond)
close(done)
// 等待发送者停止后关闭数据 channel
time.Sleep(100 * time.Millisecond)
close(data)
time.Sleep(100 * time.Millisecond)
fmt.Println()
}
// demonstrateChannelRange 演示 channel 与 range
func demonstrateChannelRange() {
fmt.Println("5. Channel 与 Range:")
// 使用 range 遍历 channel
fmt.Printf(" 使用 range 遍历 channel:\n")
numbers := make(chan int)
// 发送数据
go func() {
for i := 1; i <= 5; i++ {
numbers <- i
time.Sleep(50 * time.Millisecond)
}
close(numbers)
}()
// 使用 range 接收数据
for num := range numbers {
fmt.Printf(" 接收到数字: %d\n", num)
}
// 斐波那契数列生成器
fmt.Printf(" 斐波那契数列生成器:\n")
fib := fibonacci(10)
for value := range fib {
fmt.Printf(" 斐波那契: %d\n", value)
}
// 素数生成器
fmt.Printf(" 素数生成器:\n")
primes := sieve(30)
fmt.Printf(" 30 以内的素数: ")
for prime := range primes {
fmt.Printf("%d ", prime)
}
fmt.Printf("\n")
fmt.Println()
}
// demonstrateChannelPatterns 演示 channel 的常见模式
func demonstrateChannelPatterns() {
fmt.Println("6. Channel 的常见模式:")
// 扇出模式(一个输入,多个输出)
fmt.Printf(" 扇出模式:\n")
input := make(chan int)
output1 := make(chan int)
output2 := make(chan int)
output3 := make(chan int)
// 扇出
go fanOut(input, output1, output2, output3)
// 发送数据
go func() {
for i := 1; i <= 6; i++ {
input <- i
}
close(input)
}()
// 接收数据
var wg sync.WaitGroup
wg.Add(3)
go func() {
defer wg.Done()
for value := range output1 {
fmt.Printf(" 输出1: %d\n", value)
}
}()
go func() {
defer wg.Done()
for value := range output2 {
fmt.Printf(" 输出2: %d\n", value)
}
}()
go func() {
defer wg.Done()
for value := range output3 {
fmt.Printf(" 输出3: %d\n", value)
}
}()
wg.Wait()
// 扇入模式(多个输入,一个输出)
fmt.Printf(" 扇入模式:\n")
input1 := make(chan string)
input2 := make(chan string)
input3 := make(chan string)
// 启动输入源
go func() {
for i := 1; i <= 3; i++ {
input1 <- fmt.Sprintf("源1-%d", i)
time.Sleep(100 * time.Millisecond)
}
close(input1)
}()
go func() {
for i := 1; i <= 3; i++ {
input2 <- fmt.Sprintf("源2-%d", i)
time.Sleep(150 * time.Millisecond)
}
close(input2)
}()
go func() {
for i := 1; i <= 3; i++ {
input3 <- fmt.Sprintf("源3-%d", i)
time.Sleep(200 * time.Millisecond)
}
close(input3)
}()
// 扇入
merged := fanInMultiple(input1, input2, input3)
fmt.Printf(" 合并结果:\n")
for result := range merged {
fmt.Printf(" %s\n", result)
}
// 工作池模式
fmt.Printf(" 工作池模式:\n")
jobs := make(chan WorkJob, 10)
results := make(chan WorkResult, 10)
// 启动工作池
const numWorkers = 3
for w := 1; w <= numWorkers; w++ {
go workPoolWorker(w, jobs, results)
}
// 发送任务
for j := 1; j <= 9; j++ {
jobs <- WorkJob{ID: j, Data: fmt.Sprintf("任务-%d", j)}
}
close(jobs)
// 收集结果
for r := 1; r <= 9; r++ {
result := <-results
fmt.Printf(" %s\n", result.Message)
}
fmt.Println()
}
// demonstrateAdvancedChannelUsage 演示 channel 的高级用法
func demonstrateAdvancedChannelUsage() {
fmt.Println("7. Channel 的高级用法:")
// 超时模式
fmt.Printf(" 超时模式:\n")
slowCh := make(chan string)
go func() {
time.Sleep(300 * time.Millisecond)
slowCh <- "慢速响应"
}()
select {
case result := <-slowCh:
fmt.Printf(" 接收到: %s\n", result)
case <-time.After(200 * time.Millisecond):
fmt.Printf(" 操作超时\n")
}
// 非阻塞操作
fmt.Printf(" 非阻塞操作:\n")
nonBlockingCh := make(chan int, 1)
// 非阻塞发送
select {
case nonBlockingCh <- 42:
fmt.Printf(" 非阻塞发送成功\n")
default:
fmt.Printf(" 非阻塞发送失败channel 已满\n")
}
// 非阻塞接收
select {
case value := <-nonBlockingCh:
fmt.Printf(" 非阻塞接收成功: %d\n", value)
default:
fmt.Printf(" 非阻塞接收失败channel 为空\n")
}
// 心跳模式
fmt.Printf(" 心跳模式:\n")
heartbeat := time.NewTicker(100 * time.Millisecond)
defer heartbeat.Stop()
timeout := time.After(350 * time.Millisecond)
for {
select {
case <-heartbeat.C:
fmt.Printf(" 心跳\n")
case <-timeout:
fmt.Printf(" 心跳监控结束\n")
goto heartbeatEnd
}
}
heartbeatEnd:
// 速率限制
fmt.Printf(" 速率限制:\n")
rateLimiter := time.NewTicker(100 * time.Millisecond)
defer rateLimiter.Stop()
requests := make(chan int, 5)
for i := 1; i <= 5; i++ {
requests <- i
}
close(requests)
for req := range requests {
<-rateLimiter.C // 等待速率限制器
fmt.Printf(" 处理请求 %d (限速)\n", req)
}
fmt.Println()
}
// ========== 辅助函数和类型定义 ==========
// echoServer 简单的回声服务器
func echoServer(messages <-chan string, responses chan<- string) {
for msg := range messages {
responses <- "Echo: " + msg
}
}
// producer 生产者函数
func producer(id int, items chan<- int, count int, wg *sync.WaitGroup) {
defer wg.Done()
for i := 0; i < count; i++ {
item := id*100 + i
items <- item
fmt.Printf(" 生产者 %d 生产: %d\n", id, item)
time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond)
}
fmt.Printf(" 生产者 %d 完成\n", id)
}
// consumer 消费者函数
func consumer(id int, items <-chan int, wg *sync.WaitGroup) {
defer wg.Done()
for item := range items {
fmt.Printf(" 消费者 %d 消费: %d\n", id, item)
time.Sleep(time.Duration(rand.Intn(150)) * time.Millisecond)
}
fmt.Printf(" 消费者 %d 完成\n", id)
}
// sender 只发送函数
func sender(ch chan<- int) {
ch <- 42
close(ch)
}
// receiver 只接收函数
func receiver(ch <-chan int) {
value := <-ch
fmt.Printf(" 只接收 channel 接收到: %d\n", value)
}
// generateNumbers 生成数字
func generateNumbers(out chan<- int) {
for i := 1; i <= 5; i++ {
out <- i
}
close(out)
}
// squareNumbers 计算平方
func squareNumbers(in <-chan int, out chan<- int) {
for num := range in {
out <- num * num
}
close(out)
}
// cubeNumbers 计算立方
func cubeNumbers(in <-chan int, out chan<- int) {
for num := range in {
out <- num * num * num
}
close(out)
}
// multipleSender 多发送者函数
func multipleSender(id int, data chan<- int, done <-chan bool) {
for {
select {
case data <- id*10 + rand.Intn(10):
time.Sleep(100 * time.Millisecond)
case <-done:
fmt.Printf(" 发送者 %d 停止\n", id)
return
}
}
}
// fibonacci 斐波那契生成器
func fibonacci(n int) <-chan int {
ch := make(chan int)
go func() {
defer close(ch)
a, b := 0, 1
for i := 0; i < n; i++ {
ch <- a
a, b = b, a+b
}
}()
return ch
}
// sieve 埃拉托斯特尼筛法生成素数
func sieve(max int) <-chan int {
ch := make(chan int)
go func() {
defer close(ch)
isPrime := make([]bool, max+1)
for i := 2; i <= max; i++ {
isPrime[i] = true
}
for i := 2; i*i <= max; i++ {
if isPrime[i] {
for j := i * i; j <= max; j += i {
isPrime[j] = false
}
}
}
for i := 2; i <= max; i++ {
if isPrime[i] {
ch <- i
}
}
}()
return ch
}
// fanOut 扇出函数
func fanOut(input <-chan int, outputs ...chan<- int) {
go func() {
defer func() {
for _, output := range outputs {
close(output)
}
}()
i := 0
for value := range input {
outputs[i%len(outputs)] <- value
i++
}
}()
}
// fanInMultiple 多路扇入函数
func fanInMultiple(inputs ...<-chan string) <-chan string {
output := make(chan string)
var wg sync.WaitGroup
// 为每个输入启动一个 goroutine
for _, input := range inputs {
wg.Add(1)
go func(ch <-chan string) {
defer wg.Done()
for value := range ch {
output <- value
}
}(input)
}
// 等待所有输入完成后关闭输出
go func() {
wg.Wait()
close(output)
}()
return output
}
// WorkJob 工作任务
type WorkJob struct {
ID int
Data string
}
// WorkResult 工作结果
type WorkResult struct {
JobID int
Message string
}
// workPoolWorker 工作池工作者
func workPoolWorker(id int, jobs <-chan WorkJob, results chan<- WorkResult) {
for job := range jobs {
fmt.Printf(" 工作者 %d 开始处理任务 %d\n", id, job.ID)
time.Sleep(time.Duration(rand.Intn(200)) * time.Millisecond)
results <- WorkResult{
JobID: job.ID,
Message: fmt.Sprintf("工作者 %d 完成任务 %d: %s", id, job.ID, job.Data),
}
}
}
/*
运行这个程序:
go run 02-channels.go
学习要点:
1. Channel 是 Go 语言中 goroutine 间通信的主要方式
2. Channel 有无缓冲和有缓冲两种类型
3. Channel 具有方向性,可以限制为只发送或只接收
4. Channel 可以被关闭,关闭后不能再发送数据
5. 使用 range 可以方便地遍历 channel 中的数据
Channel 的特性:
1. 类型安全:只能传递指定类型的数据
2. 同步机制:无缓冲 channel 提供同步语义
3. 异步机制:有缓冲 channel 提供异步语义
4. 方向性:可以限制 channel 的使用方向
5. 可关闭:发送方可以关闭 channel 通知接收方
Channel 类型:
1. 无缓冲 channelmake(chan T)
2. 有缓冲 channelmake(chan T, size)
3. 只发送 channelchan<- T
4. 只接收 channel<-chan T
5. 双向 channelchan T
Channel 操作:
1. 发送ch <- value
2. 接收value := <-ch
3. 接收并检查value, ok := <-ch
4. 关闭close(ch)
5. 遍历for value := range ch
常见模式:
1. 生产者-消费者:使用缓冲 channel 解耦生产和消费
2. 扇出:一个输入分发到多个输出
3. 扇入:多个输入合并到一个输出
4. 管道:链式处理数据
5. 工作池:固定数量的 worker 处理任务
高级用法:
1. 超时控制:使用 time.After
2. 非阻塞操作:使用 select 的 default 分支
3. 心跳机制:使用 time.Ticker
4. 速率限制:控制操作频率
5. 信号通知:使用 channel 作为信号
最佳实践:
1. 发送方负责关闭 channel
2. 不要从接收方关闭 channel
3. 不要关闭已关闭的 channel
4. 使用 range 遍历 channel
5. 使用 select 处理多个 channel
注意事项:
1. 向已关闭的 channel 发送数据会 panic
2. 关闭已关闭的 channel 会 panic
3. 从已关闭的 channel 接收会得到零值
4. nil channel 的发送和接收都会阻塞
5. 无缓冲 channel 的发送和接收必须同时准备好
性能考虑:
1. 无缓冲 channel 有同步开销
2. 有缓冲 channel 可以减少阻塞
3. Channel 操作比直接内存访问慢
4. 合理选择缓冲区大小
5. 避免创建过多的 channel
*/

View File

@@ -0,0 +1,880 @@
/*
03-select.go - Go 语言 Select 语句详解
学习目标:
1. 理解 select 语句的概念和作用
2. 掌握 select 的基本语法和用法
3. 学会使用 select 处理多个 channel
4. 了解 select 的非阻塞操作
5. 掌握 select 的常见应用模式
知识点:
- select 语句的基本语法
- select 的随机选择特性
- default 分支的使用
- select 与超时控制
- select 的常见模式
- select 的最佳实践
*/
package main
import (
"fmt"
"math/rand"
"reflect"
"strings"
"time"
)
func main() {
fmt.Println("=== Go 语言 Select 语句详解 ===\n")
// 演示基本的 select 语句
demonstrateBasicSelect()
// 演示 select 的随机选择
demonstrateRandomSelection()
// 演示 select 的非阻塞操作
demonstrateNonBlockingSelect()
// 演示 select 的超时控制
demonstrateSelectTimeout()
// 演示 select 的常见模式
demonstrateSelectPatterns()
// 演示 select 的高级用法
demonstrateAdvancedSelect()
// 演示 select 的实际应用
demonstratePracticalSelect()
}
// demonstrateBasicSelect 演示基本的 select 语句
func demonstrateBasicSelect() {
fmt.Println("1. 基本的 Select 语句:")
// select 的基本概念
fmt.Printf(" Select 的基本概念:\n")
fmt.Printf(" - 类似于 switch但用于 channel 操作\n")
fmt.Printf(" - 可以同时等待多个 channel 操作\n")
fmt.Printf(" - 随机选择一个可执行的 case\n")
fmt.Printf(" - 如果没有 case 可执行,会阻塞等待\n")
fmt.Printf(" - default 分支提供非阻塞行为\n")
// 基本 select 示例
fmt.Printf(" 基本 select 示例:\n")
ch1 := make(chan string)
ch2 := make(chan string)
// 启动两个 goroutine 发送数据
go func() {
time.Sleep(100 * time.Millisecond)
ch1 <- "来自 channel 1"
}()
go func() {
time.Sleep(200 * time.Millisecond)
ch2 <- "来自 channel 2"
}()
// 使用 select 等待任一 channel
select {
case msg1 := <-ch1:
fmt.Printf(" 接收到: %s\n", msg1)
case msg2 := <-ch2:
fmt.Printf(" 接收到: %s\n", msg2)
}
// 接收剩余的消息
select {
case msg1 := <-ch1:
fmt.Printf(" 接收到: %s\n", msg1)
case msg2 := <-ch2:
fmt.Printf(" 接收到: %s\n", msg2)
}
// 多个 channel 的 select
fmt.Printf(" 多个 channel 的 select:\n")
numbers := make(chan int)
strings := make(chan string)
booleans := make(chan bool)
// 启动发送者
go func() {
numbers <- 42
strings <- "Hello"
booleans <- true
}()
// 接收所有消息
for i := 0; i < 3; i++ {
select {
case num := <-numbers:
fmt.Printf(" 接收到数字: %d\n", num)
case str := <-strings:
fmt.Printf(" 接收到字符串: %s\n", str)
case b := <-booleans:
fmt.Printf(" 接收到布尔值: %t\n", b)
}
}
fmt.Println()
}
// demonstrateRandomSelection 演示 select 的随机选择
func demonstrateRandomSelection() {
fmt.Println("2. Select 的随机选择:")
// 多个 case 同时就绪时的随机选择
fmt.Printf(" 多个 case 同时就绪时的随机选择:\n")
ch1 := make(chan string, 1)
ch2 := make(chan string, 1)
ch3 := make(chan string, 1)
// 预先填充所有 channel
ch1 <- "Channel 1"
ch2 <- "Channel 2"
ch3 <- "Channel 3"
// 多次执行 select观察随机选择
fmt.Printf(" 执行 10 次 select观察随机选择:\n")
for i := 0; i < 10; i++ {
// 重新填充 channel
select {
case <-ch1:
case <-ch2:
case <-ch3:
}
ch1 <- "Channel 1"
ch2 <- "Channel 2"
ch3 <- "Channel 3"
select {
case msg := <-ch1:
fmt.Printf(" 第 %d 次: %s\n", i+1, msg)
case msg := <-ch2:
fmt.Printf(" 第 %d 次: %s\n", i+1, msg)
case msg := <-ch3:
fmt.Printf(" 第 %d 次: %s\n", i+1, msg)
}
}
// 公平调度示例
fmt.Printf(" 公平调度示例:\n")
worker1 := make(chan string, 5)
worker2 := make(chan string, 5)
// 填充任务
for i := 1; i <= 5; i++ {
worker1 <- fmt.Sprintf("Worker1-Task%d", i)
worker2 <- fmt.Sprintf("Worker2-Task%d", i)
}
// 公平地处理两个 worker 的任务
for i := 0; i < 10; i++ {
select {
case task := <-worker1:
fmt.Printf(" 处理: %s\n", task)
case task := <-worker2:
fmt.Printf(" 处理: %s\n", task)
}
}
fmt.Println()
}
// demonstrateNonBlockingSelect 演示 select 的非阻塞操作
func demonstrateNonBlockingSelect() {
fmt.Println("3. Select 的非阻塞操作:")
// 使用 default 分支实现非阻塞
fmt.Printf(" 使用 default 分支实现非阻塞:\n")
ch := make(chan string)
// 非阻塞接收
select {
case msg := <-ch:
fmt.Printf(" 接收到消息: %s\n", msg)
default:
fmt.Printf(" 没有消息可接收\n")
}
// 非阻塞发送
select {
case ch <- "Hello":
fmt.Printf(" 消息发送成功\n")
default:
fmt.Printf(" 消息发送失败channel 未准备好\n")
}
// 非阻塞发送到缓冲 channel
fmt.Printf(" 非阻塞发送到缓冲 channel:\n")
buffered := make(chan int, 2)
// 发送到未满的缓冲 channel
for i := 1; i <= 3; i++ {
select {
case buffered <- i:
fmt.Printf(" 成功发送: %d\n", i)
default:
fmt.Printf(" 发送失败: %d (channel 已满)\n", i)
}
}
// 非阻塞接收
for i := 0; i < 3; i++ {
select {
case value := <-buffered:
fmt.Printf(" 接收到: %d\n", value)
default:
fmt.Printf(" 没有数据可接收\n")
}
}
// 轮询模式
fmt.Printf(" 轮询模式:\n")
status := make(chan string, 1)
// 启动状态更新器
go func() {
statuses := []string{"初始化", "运行中", "暂停", "完成"}
for _, s := range statuses {
time.Sleep(150 * time.Millisecond)
select {
case status <- s:
default:
// 如果 channel 满了,跳过这次更新
}
}
}()
// 轮询状态
for i := 0; i < 10; i++ {
select {
case s := <-status:
fmt.Printf(" 状态更新: %s\n", s)
default:
fmt.Printf(" 状态检查: 无更新\n")
}
time.Sleep(100 * time.Millisecond)
}
fmt.Println()
}
// demonstrateSelectTimeout 演示 select 的超时控制
func demonstrateSelectTimeout() {
fmt.Println("4. Select 的超时控制:")
// 基本超时控制
fmt.Printf(" 基本超时控制:\n")
slowCh := make(chan string)
// 启动一个慢速响应的 goroutine
go func() {
time.Sleep(300 * time.Millisecond)
slowCh <- "慢速响应"
}()
select {
case result := <-slowCh:
fmt.Printf(" 接收到结果: %s\n", result)
case <-time.After(200 * time.Millisecond):
fmt.Printf(" 操作超时\n")
}
// 多级超时控制
fmt.Printf(" 多级超时控制:\n")
fastCh := make(chan string)
mediumCh := make(chan string)
slowCh2 := make(chan string)
// 启动不同速度的响应
go func() {
time.Sleep(50 * time.Millisecond)
fastCh <- "快速响应"
}()
go func() {
time.Sleep(150 * time.Millisecond)
mediumCh <- "中速响应"
}()
go func() {
time.Sleep(350 * time.Millisecond)
slowCh2 <- "慢速响应"
}()
timeout1 := time.After(100 * time.Millisecond)
timeout2 := time.After(200 * time.Millisecond)
timeout3 := time.After(300 * time.Millisecond)
for i := 0; i < 3; i++ {
select {
case result := <-fastCh:
fmt.Printf(" 快速: %s\n", result)
case result := <-mediumCh:
fmt.Printf(" 中速: %s\n", result)
case result := <-slowCh2:
fmt.Printf(" 慢速: %s\n", result)
case <-timeout1:
fmt.Printf(" 第一级超时 (100ms)\n")
timeout1 = nil // 防止重复触发
case <-timeout2:
fmt.Printf(" 第二级超时 (200ms)\n")
timeout2 = nil
case <-timeout3:
fmt.Printf(" 第三级超时 (300ms)\n")
timeout3 = nil
}
}
// 心跳超时检测
fmt.Printf(" 心跳超时检测:\n")
heartbeat := make(chan bool)
// 启动心跳发送器
go func() {
ticker := time.NewTicker(80 * time.Millisecond)
defer ticker.Stop()
for i := 0; i < 5; i++ {
select {
case <-ticker.C:
heartbeat <- true
}
}
}()
// 监控心跳
for i := 0; i < 8; i++ {
select {
case <-heartbeat:
fmt.Printf(" 心跳正常 %d\n", i+1)
case <-time.After(100 * time.Millisecond):
fmt.Printf(" 心跳超时 %d\n", i+1)
}
}
fmt.Println()
}
// demonstrateSelectPatterns 演示 select 的常见模式
func demonstrateSelectPatterns() {
fmt.Println("5. Select 的常见模式:")
// 扇入模式
fmt.Printf(" 扇入模式:\n")
input1 := make(chan int)
input2 := make(chan int)
input3 := make(chan int)
// 启动数据源
go func() {
for i := 1; i <= 3; i++ {
input1 <- i
time.Sleep(100 * time.Millisecond)
}
close(input1)
}()
go func() {
for i := 10; i <= 12; i++ {
input2 <- i
time.Sleep(150 * time.Millisecond)
}
close(input2)
}()
go func() {
for i := 100; i <= 102; i++ {
input3 <- i
time.Sleep(200 * time.Millisecond)
}
close(input3)
}()
// 扇入合并
for {
select {
case val, ok := <-input1:
if ok {
fmt.Printf(" 来源1: %d\n", val)
} else {
input1 = nil
}
case val, ok := <-input2:
if ok {
fmt.Printf(" 来源2: %d\n", val)
} else {
input2 = nil
}
case val, ok := <-input3:
if ok {
fmt.Printf(" 来源3: %d\n", val)
} else {
input3 = nil
}
}
// 所有 channel 都关闭时退出
if input1 == nil && input2 == nil && input3 == nil {
break
}
}
// 优雅关闭模式
fmt.Printf(" 优雅关闭模式:\n")
data := make(chan int)
done := make(chan bool)
// 启动工作 goroutine
go func() {
defer close(data)
for i := 1; i <= 10; i++ {
select {
case data <- i:
fmt.Printf(" 发送数据: %d\n", i)
time.Sleep(50 * time.Millisecond)
case <-done:
fmt.Printf(" 接收到关闭信号,停止发送\n")
return
}
}
}()
// 接收数据,然后发送关闭信号
count := 0
for value := range data {
fmt.Printf(" 接收数据: %d\n", value)
count++
if count >= 5 {
fmt.Printf(" 发送关闭信号\n")
close(done)
}
}
// 多路复用模式
fmt.Printf(" 多路复用模式:\n")
requests := make(chan Request, 5)
responses := make(chan Response, 5)
errors := make(chan error, 5)
// 启动请求处理器
go requestProcessor(requests, responses, errors)
// 发送请求
for i := 1; i <= 5; i++ {
requests <- Request{ID: i, Data: fmt.Sprintf("请求-%d", i)}
}
close(requests)
// 处理响应和错误
for i := 0; i < 5; i++ {
select {
case resp := <-responses:
fmt.Printf(" 响应: ID=%d, Result=%s\n", resp.ID, resp.Result)
case err := <-errors:
fmt.Printf(" 错误: %v\n", err)
}
}
fmt.Println()
}
// demonstrateAdvancedSelect 演示 select 的高级用法
func demonstrateAdvancedSelect() {
fmt.Println("6. Select 的高级用法:")
// 动态 case 选择
fmt.Printf(" 动态 case 选择:\n")
channels := []chan int{
make(chan int, 1),
make(chan int, 1),
make(chan int, 1),
}
// 填充数据
for i, ch := range channels {
ch <- (i + 1) * 10
}
// 动态选择 channel
for i := 0; i < 3; i++ {
selectFromChannels(channels)
}
// 优先级选择
fmt.Printf(" 优先级选择:\n")
highPriority := make(chan string, 2)
lowPriority := make(chan string, 2)
// 填充不同优先级的任务
highPriority <- "高优先级任务1"
lowPriority <- "低优先级任务1"
highPriority <- "高优先级任务2"
lowPriority <- "低优先级任务2"
// 优先处理高优先级任务
for i := 0; i < 4; i++ {
select {
case task := <-highPriority:
fmt.Printf(" 处理: %s\n", task)
default:
select {
case task := <-lowPriority:
fmt.Printf(" 处理: %s\n", task)
default:
fmt.Printf(" 没有任务\n")
}
}
}
// 速率限制
fmt.Printf(" 速率限制:\n")
rateLimiter := time.NewTicker(100 * time.Millisecond)
defer rateLimiter.Stop()
tasks := make(chan string, 5)
for i := 1; i <= 5; i++ {
tasks <- fmt.Sprintf("任务%d", i)
}
close(tasks)
for task := range tasks {
select {
case <-rateLimiter.C:
fmt.Printf(" 执行: %s\n", task)
}
}
// 断路器模式
fmt.Printf(" 断路器模式:\n")
service := make(chan string)
failures := 0
maxFailures := 3
// 模拟服务调用
go func() {
defer close(service)
for i := 1; i <= 6; i++ {
time.Sleep(50 * time.Millisecond)
if rand.Float32() < 0.5 { // 50% 失败率
service <- fmt.Sprintf("成功响应%d", i)
} else {
service <- "ERROR"
}
}
}()
for response := range service {
if response == "ERROR" {
failures++
fmt.Printf(" 服务失败 (%d/%d)\n", failures, maxFailures)
if failures >= maxFailures {
fmt.Printf(" 断路器打开,停止调用服务\n")
break
}
} else {
failures = 0 // 重置失败计数
fmt.Printf(" %s\n", response)
}
}
fmt.Println()
}
// demonstratePracticalSelect 演示 select 的实际应用
func demonstratePracticalSelect() {
fmt.Println("7. Select 的实际应用:")
// Web 服务器超时处理
fmt.Printf(" Web 服务器超时处理:\n")
requests := make(chan WebRequest, 3)
// 模拟 Web 请求
go func() {
requests <- WebRequest{ID: 1, URL: "/api/fast", ProcessTime: 50 * time.Millisecond}
requests <- WebRequest{ID: 2, URL: "/api/slow", ProcessTime: 300 * time.Millisecond}
requests <- WebRequest{ID: 3, URL: "/api/medium", ProcessTime: 150 * time.Millisecond}
close(requests)
}()
for req := range requests {
handleWebRequest(req)
}
// 数据库连接池
fmt.Printf(" 数据库连接池:\n")
connectionPool := make(chan *DBConnection, 2)
// 初始化连接池
for i := 1; i <= 2; i++ {
connectionPool <- &DBConnection{ID: i}
}
// 模拟数据库操作
for i := 1; i <= 5; i++ {
go performDBOperation(i, connectionPool)
}
time.Sleep(500 * time.Millisecond)
// 消息队列处理
fmt.Printf(" 消息队列处理:\n")
messageQueue := make(chan Message, 10)
deadLetterQueue := make(chan Message, 10)
// 启动消息处理器
go messageProcessor(messageQueue, deadLetterQueue)
// 发送消息
messages := []Message{
{ID: 1, Content: "正常消息1", RetryCount: 0},
{ID: 2, Content: "错误消息", RetryCount: 0},
{ID: 3, Content: "正常消息2", RetryCount: 0},
}
for _, msg := range messages {
messageQueue <- msg
}
close(messageQueue)
// 处理死信队列
time.Sleep(200 * time.Millisecond)
close(deadLetterQueue)
fmt.Printf(" 死信队列中的消息:\n")
for deadMsg := range deadLetterQueue {
fmt.Printf(" 死信: ID=%d, Content=%s, Retries=%d\n",
deadMsg.ID, deadMsg.Content, deadMsg.RetryCount)
}
fmt.Println()
}
// ========== 辅助函数和类型定义 ==========
// Request 请求结构
type Request struct {
ID int
Data string
}
// Response 响应结构
type Response struct {
ID int
Result string
}
// requestProcessor 请求处理器
func requestProcessor(requests <-chan Request, responses chan<- Response, errors chan<- error) {
for req := range requests {
// 模拟处理时间
time.Sleep(50 * time.Millisecond)
// 模拟随机错误
if rand.Float32() < 0.2 { // 20% 错误率
errors <- fmt.Errorf("处理请求 %d 失败", req.ID)
} else {
responses <- Response{
ID: req.ID,
Result: fmt.Sprintf("处理完成: %s", req.Data),
}
}
}
close(responses)
close(errors)
}
// selectFromChannels 动态选择 channel
func selectFromChannels(channels []chan int) {
// 构建 select cases
cases := make([]reflect.SelectCase, len(channels))
for i, ch := range channels {
cases[i] = reflect.SelectCase{
Dir: reflect.SelectRecv,
Chan: reflect.ValueOf(ch),
}
}
// 执行 select
chosen, value, ok := reflect.Select(cases)
if ok {
fmt.Printf(" 从 channel %d 接收到: %d\n", chosen, value.Int())
} else {
fmt.Printf(" channel %d 已关闭\n", chosen)
}
}
// WebRequest Web 请求结构
type WebRequest struct {
ID int
URL string
ProcessTime time.Duration
}
// handleWebRequest 处理 Web 请求
func handleWebRequest(req WebRequest) {
fmt.Printf(" 处理请求 %d: %s\n", req.ID, req.URL)
result := make(chan string)
// 启动请求处理
go func() {
time.Sleep(req.ProcessTime)
result <- fmt.Sprintf("请求 %d 处理完成", req.ID)
}()
// 设置超时
select {
case response := <-result:
fmt.Printf(" %s\n", response)
case <-time.After(200 * time.Millisecond):
fmt.Printf(" 请求 %d 超时\n", req.ID)
}
}
// DBConnection 数据库连接
type DBConnection struct {
ID int
}
// performDBOperation 执行数据库操作
func performDBOperation(operationID int, pool chan *DBConnection) {
select {
case conn := <-pool:
fmt.Printf(" 操作 %d 获取连接 %d\n", operationID, conn.ID)
// 模拟数据库操作
time.Sleep(100 * time.Millisecond)
fmt.Printf(" 操作 %d 完成,释放连接 %d\n", operationID, conn.ID)
pool <- conn
case <-time.After(50 * time.Millisecond):
fmt.Printf(" 操作 %d 获取连接超时\n", operationID)
}
}
// Message 消息结构
type Message struct {
ID int
Content string
RetryCount int
}
// messageProcessor 消息处理器
func messageProcessor(messages <-chan Message, deadLetter chan<- Message) {
for msg := range messages {
fmt.Printf(" 处理消息 %d: %s\n", msg.ID, msg.Content)
// 模拟处理
success := !strings.Contains(msg.Content, "错误")
if success {
fmt.Printf(" 消息 %d 处理成功\n", msg.ID)
} else {
msg.RetryCount++
if msg.RetryCount < 3 {
fmt.Printf(" 消息 %d 处理失败,重试 %d\n", msg.ID, msg.RetryCount)
// 在实际应用中,这里会重新放入队列
} else {
fmt.Printf(" 消息 %d 重试次数超限,放入死信队列\n", msg.ID)
deadLetter <- msg
}
}
}
}
/*
运行这个程序:
go run 03-select.go
学习要点:
1. Select 语句用于处理多个 channel 操作
2. Select 会随机选择一个可执行的 case
3. Default 分支提供非阻塞行为
4. Select 常用于超时控制和多路复用
5. Select 是实现复杂并发模式的重要工具
Select 的特性:
1. 多路选择:可以同时等待多个 channel 操作
2. 随机选择:当多个 case 同时就绪时随机选择
3. 阻塞行为:没有 case 就绪时会阻塞等待
4. 非阻塞default 分支提供非阻塞行为
5. 公平调度:避免某个 channel 被饿死
Select 语法:
1. 基本语法select { case <-ch: ... }
2. 接收操作case value := <-ch:
3. 发送操作case ch <- value:
4. 默认分支default:
5. 超时控制case <-time.After(duration):
常见模式:
1. 超时控制:使用 time.After 设置超时
2. 非阻塞操作:使用 default 分支
3. 扇入合并:合并多个输入源
4. 优雅关闭:使用 done channel 通知退出
5. 多路复用:同时处理多种类型的消息
高级用法:
1. 动态 case使用 reflect.Select 动态构建 case
2. 优先级选择:嵌套 select 实现优先级
3. 速率限制:结合 time.Ticker 控制频率
4. 断路器:实现服务降级和熔断
5. 负载均衡:在多个服务间分发请求
实际应用:
1. Web 服务器:请求超时处理
2. 数据库:连接池管理
3. 消息队列:消息路由和处理
4. 微服务:服务间通信
5. 监控系统:多源数据收集
最佳实践:
1. 避免在 select 中执行耗时操作
2. 合理设置超时时间
3. 正确处理 channel 关闭
4. 使用 default 避免死锁
5. 注意 select 的随机性
性能考虑:
1. Select 的开销比单个 channel 操作大
2. Case 数量影响性能
3. 随机选择有一定开销
4. 合理使用缓冲 channel
5. 避免过度复杂的 select 结构
注意事项:
1. Select 中的 case 必须是 channel 操作
2. nil channel 的 case 永远不会被选中
3. 关闭的 channel 可以被选中
4. Default 分支不能与其他阻塞操作共存
5. Select 语句本身不是循环
*/

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff