继续
This commit is contained in:
738
golang-learning/06-concurrency/01-goroutines.go
Normal file
738
golang-learning/06-concurrency/01-goroutines.go
Normal 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
|
||||
}()
|
||||
// 如果不读取 channel,goroutine 会泄漏
|
||||
|
||||
// 正确示例:使用缓冲 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 中使用全局变量
|
||||
*/
|
840
golang-learning/06-concurrency/02-channels.go
Normal file
840
golang-learning/06-concurrency/02-channels.go
Normal 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. 无缓冲 channel:make(chan T)
|
||||
2. 有缓冲 channel:make(chan T, size)
|
||||
3. 只发送 channel:chan<- T
|
||||
4. 只接收 channel:<-chan T
|
||||
5. 双向 channel:chan 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
|
||||
*/
|
880
golang-learning/06-concurrency/03-select.go
Normal file
880
golang-learning/06-concurrency/03-select.go
Normal 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 语句本身不是循环
|
||||
*/
|
1183
golang-learning/06-concurrency/04-sync-package.go
Normal file
1183
golang-learning/06-concurrency/04-sync-package.go
Normal file
File diff suppressed because it is too large
Load Diff
1561
golang-learning/06-concurrency/05-worker-pools.go
Normal file
1561
golang-learning/06-concurrency/05-worker-pools.go
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user