继续
This commit is contained in:
@@ -59,27 +59,27 @@
|
|||||||
- _需求: 4.1, 4.2, 4.3_
|
- _需求: 4.1, 4.2, 4.3_
|
||||||
|
|
||||||
- [ ] 6. 实现接口学习模块
|
- [ ] 6. 实现接口学习模块
|
||||||
- [ ] 6.1 创建接口基础示例
|
- [x] 6.1 创建接口基础示例
|
||||||
- 编写 05-interfaces/01-basic-interfaces.go,展示接口的定义和实现
|
- 编写 05-interfaces/01-basic-interfaces.go,展示接口的定义和实现
|
||||||
- 编写 05-interfaces/02-empty-interface.go,演示空接口的使用
|
- 编写 05-interfaces/02-empty-interface.go,演示空接口的使用
|
||||||
- 编写 05-interfaces/03-type-assertions.go,展示类型断言的使用
|
- 编写 05-interfaces/03-type-assertions.go,展示类型断言的使用
|
||||||
- _需求: 3.2_
|
- _需求: 3.2_
|
||||||
|
|
||||||
- [ ] 7. 实现并发编程学习模块
|
- [ ] 7. 实现并发编程学习模块
|
||||||
- [ ] 7.1 创建 Goroutine 和 Channel 示例
|
- [x] 7.1 创建 Goroutine 和 Channel 示例
|
||||||
- 编写 06-concurrency/01-goroutines.go,展示 goroutine 的基础使用
|
- 编写 06-concurrency/01-goroutines.go,展示 goroutine 的基础使用
|
||||||
- 编写 06-concurrency/02-channels.go,演示 channel 的各种操作
|
- 编写 06-concurrency/02-channels.go,演示 channel 的各种操作
|
||||||
- 编写 06-concurrency/03-select.go,展示 select 语句的使用
|
- 编写 06-concurrency/03-select.go,展示 select 语句的使用
|
||||||
- _需求: 5.1, 5.2, 5.3_
|
- _需求: 5.1, 5.2, 5.3_
|
||||||
|
|
||||||
- [ ] 7.2 创建同步和高级并发示例
|
- [x] 7.2 创建同步和高级并发示例
|
||||||
- 编写 06-concurrency/04-sync-package.go,展示 sync 包的使用方法
|
- 编写 06-concurrency/04-sync-package.go,展示 sync 包的使用方法
|
||||||
- 编写 06-concurrency/05-worker-pools.go,演示工作池模式
|
- 编写 06-concurrency/05-worker-pools.go,演示工作池模式
|
||||||
- 提供避免竞态条件的最佳实践示例
|
- 提供避免竞态条件的最佳实践示例
|
||||||
- _需求: 5.1, 5.2, 5.3_
|
- _需求: 5.1, 5.2, 5.3_
|
||||||
|
|
||||||
- [ ] 8. 实现错误处理学习模块
|
- [ ] 8. 实现错误处理学习模块
|
||||||
- [ ] 8.1 创建错误处理示例
|
- [x] 8.1 创建错误处理示例
|
||||||
- 编写 07-error-handling/01-basic-errors.go,展示基本错误处理
|
- 编写 07-error-handling/01-basic-errors.go,展示基本错误处理
|
||||||
- 编写 07-error-handling/02-custom-errors.go,演示自定义错误类型
|
- 编写 07-error-handling/02-custom-errors.go,演示自定义错误类型
|
||||||
- 编写 07-error-handling/03-panic-recover.go,展示 panic 和 recover 的使用
|
- 编写 07-error-handling/03-panic-recover.go,展示 panic 和 recover 的使用
|
||||||
|
1340
golang-learning/05-interfaces/01-basic-interfaces.go
Normal file
1340
golang-learning/05-interfaces/01-basic-interfaces.go
Normal file
File diff suppressed because it is too large
Load Diff
981
golang-learning/05-interfaces/02-empty-interface.go
Normal file
981
golang-learning/05-interfaces/02-empty-interface.go
Normal file
@@ -0,0 +1,981 @@
|
|||||||
|
/*
|
||||||
|
02-empty-interface.go - Go 语言空接口详解
|
||||||
|
|
||||||
|
学习目标:
|
||||||
|
1. 理解空接口的概念和特性
|
||||||
|
2. 掌握空接口的使用场景
|
||||||
|
3. 学会空接口的类型断言
|
||||||
|
4. 了解空接口的性能考虑
|
||||||
|
5. 掌握空接口的实际应用
|
||||||
|
|
||||||
|
知识点:
|
||||||
|
- 空接口的定义 interface{}
|
||||||
|
- 空接口可以持有任何类型的值
|
||||||
|
- 空接口的类型断言和类型判断
|
||||||
|
- 空接口在泛型编程中的应用
|
||||||
|
- 空接口的性能影响
|
||||||
|
- 空接口的最佳实践
|
||||||
|
*/
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
fmt.Println("=== Go 语言空接口详解 ===\n")
|
||||||
|
|
||||||
|
// 演示空接口的基本概念
|
||||||
|
demonstrateBasicEmptyInterface()
|
||||||
|
|
||||||
|
// 演示空接口的类型断言
|
||||||
|
demonstrateTypeAssertion()
|
||||||
|
|
||||||
|
// 演示空接口的类型判断
|
||||||
|
demonstrateTypeSwitch()
|
||||||
|
|
||||||
|
// 演示空接口的实际应用
|
||||||
|
demonstratePracticalUsage()
|
||||||
|
|
||||||
|
// 演示空接口的高级用法
|
||||||
|
demonstrateAdvancedUsage()
|
||||||
|
|
||||||
|
// 演示空接口的性能考虑
|
||||||
|
demonstratePerformanceConsiderations()
|
||||||
|
|
||||||
|
// 演示空接口的最佳实践
|
||||||
|
demonstrateBestPractices()
|
||||||
|
}
|
||||||
|
|
||||||
|
// demonstrateBasicEmptyInterface 演示空接口的基本概念
|
||||||
|
func demonstrateBasicEmptyInterface() {
|
||||||
|
fmt.Println("1. 空接口的基本概念:")
|
||||||
|
|
||||||
|
// 空接口的基本特性
|
||||||
|
fmt.Printf(" 空接口的基本特性:\n")
|
||||||
|
fmt.Printf(" - interface{} 不包含任何方法\n")
|
||||||
|
fmt.Printf(" - 可以持有任何类型的值\n")
|
||||||
|
fmt.Printf(" - 相当于其他语言中的 Object 或 Any\n")
|
||||||
|
fmt.Printf(" - 零值是 nil\n")
|
||||||
|
fmt.Printf(" - 运行时类型信息保存在接口中\n")
|
||||||
|
|
||||||
|
// 基本空接口示例
|
||||||
|
fmt.Printf(" 基本空接口示例:\n")
|
||||||
|
|
||||||
|
var empty interface{}
|
||||||
|
fmt.Printf(" 空接口零值: %v\n", empty)
|
||||||
|
fmt.Printf(" 空接口 == nil: %t\n", empty == nil)
|
||||||
|
|
||||||
|
// 存储不同类型的值
|
||||||
|
empty = 42
|
||||||
|
fmt.Printf(" 存储整数: %v (类型: %T)\n", empty, empty)
|
||||||
|
|
||||||
|
empty = "Hello, World!"
|
||||||
|
fmt.Printf(" 存储字符串: %v (类型: %T)\n", empty, empty)
|
||||||
|
|
||||||
|
empty = 3.14159
|
||||||
|
fmt.Printf(" 存储浮点数: %v (类型: %T)\n", empty, empty)
|
||||||
|
|
||||||
|
empty = true
|
||||||
|
fmt.Printf(" 存储布尔值: %v (类型: %T)\n", empty, empty)
|
||||||
|
|
||||||
|
empty = []int{1, 2, 3, 4, 5}
|
||||||
|
fmt.Printf(" 存储切片: %v (类型: %T)\n", empty, empty)
|
||||||
|
|
||||||
|
empty = map[string]int{"a": 1, "b": 2}
|
||||||
|
fmt.Printf(" 存储映射: %v (类型: %T)\n", empty, empty)
|
||||||
|
|
||||||
|
// 存储结构体
|
||||||
|
type Person struct {
|
||||||
|
Name string
|
||||||
|
Age int
|
||||||
|
}
|
||||||
|
|
||||||
|
empty = Person{Name: "Alice", Age: 30}
|
||||||
|
fmt.Printf(" 存储结构体: %v (类型: %T)\n", empty, empty)
|
||||||
|
|
||||||
|
// 存储指针
|
||||||
|
person := &Person{Name: "Bob", Age: 25}
|
||||||
|
empty = person
|
||||||
|
fmt.Printf(" 存储指针: %v (类型: %T)\n", empty, empty)
|
||||||
|
|
||||||
|
// 存储函数
|
||||||
|
empty = func(x int) int { return x * 2 }
|
||||||
|
fmt.Printf(" 存储函数: %v (类型: %T)\n", empty, empty)
|
||||||
|
|
||||||
|
fmt.Println()
|
||||||
|
}
|
||||||
|
|
||||||
|
// demonstrateTypeAssertion 演示空接口的类型断言
|
||||||
|
func demonstrateTypeAssertion() {
|
||||||
|
fmt.Println("2. 空接口的类型断言:")
|
||||||
|
|
||||||
|
fmt.Printf(" 类型断言用于从接口中提取具体类型的值\n")
|
||||||
|
|
||||||
|
// 基本类型断言
|
||||||
|
fmt.Printf(" 基本类型断言:\n")
|
||||||
|
|
||||||
|
var value interface{} = "Hello, Go!"
|
||||||
|
|
||||||
|
// 安全的类型断言
|
||||||
|
if str, ok := value.(string); ok {
|
||||||
|
fmt.Printf(" 类型断言成功: %s (长度: %d)\n", str, len(str))
|
||||||
|
} else {
|
||||||
|
fmt.Printf(" 类型断言失败\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 不安全的类型断言(可能 panic)
|
||||||
|
str := value.(string)
|
||||||
|
fmt.Printf(" 直接类型断言: %s\n", str)
|
||||||
|
|
||||||
|
// 类型断言失败的情况
|
||||||
|
fmt.Printf(" 类型断言失败的情况:\n")
|
||||||
|
|
||||||
|
value = 42
|
||||||
|
if str, ok := value.(string); ok {
|
||||||
|
fmt.Printf(" 字符串断言成功: %s\n", str)
|
||||||
|
} else {
|
||||||
|
fmt.Printf(" 字符串断言失败,实际类型: %T\n", value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 断言为整数
|
||||||
|
if num, ok := value.(int); ok {
|
||||||
|
fmt.Printf(" 整数断言成功: %d\n", num)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 多种类型断言
|
||||||
|
fmt.Printf(" 多种类型断言:\n")
|
||||||
|
|
||||||
|
values := []interface{}{
|
||||||
|
42,
|
||||||
|
"Hello",
|
||||||
|
3.14,
|
||||||
|
true,
|
||||||
|
[]int{1, 2, 3},
|
||||||
|
map[string]int{"key": 100},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, v := range values {
|
||||||
|
fmt.Printf(" 值 %d (%v):\n", i+1, v)
|
||||||
|
|
||||||
|
// 尝试不同的类型断言
|
||||||
|
if intVal, ok := v.(int); ok {
|
||||||
|
fmt.Printf(" 整数: %d\n", intVal)
|
||||||
|
} else if strVal, ok := v.(string); ok {
|
||||||
|
fmt.Printf(" 字符串: %s\n", strVal)
|
||||||
|
} else if floatVal, ok := v.(float64); ok {
|
||||||
|
fmt.Printf(" 浮点数: %.2f\n", floatVal)
|
||||||
|
} else if boolVal, ok := v.(bool); ok {
|
||||||
|
fmt.Printf(" 布尔值: %t\n", boolVal)
|
||||||
|
} else {
|
||||||
|
fmt.Printf(" 其他类型: %T\n", v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println()
|
||||||
|
}
|
||||||
|
|
||||||
|
// demonstrateTypeSwitch 演示空接口的类型判断
|
||||||
|
func demonstrateTypeSwitch() {
|
||||||
|
fmt.Println("3. 空接口的类型判断:")
|
||||||
|
|
||||||
|
fmt.Printf(" 类型 switch 是处理空接口的优雅方式\n")
|
||||||
|
|
||||||
|
// 基本类型 switch
|
||||||
|
fmt.Printf(" 基本类型 switch:\n")
|
||||||
|
|
||||||
|
values := []interface{}{
|
||||||
|
42,
|
||||||
|
"Hello, World!",
|
||||||
|
3.14159,
|
||||||
|
true,
|
||||||
|
[]int{1, 2, 3, 4, 5},
|
||||||
|
map[string]int{"a": 1, "b": 2},
|
||||||
|
nil,
|
||||||
|
struct{ Name string }{"Alice"},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, value := range values {
|
||||||
|
fmt.Printf(" 值 %d: ", i+1)
|
||||||
|
processValue(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 复杂类型 switch
|
||||||
|
fmt.Printf(" 复杂类型 switch:\n")
|
||||||
|
|
||||||
|
complexValues := []interface{}{
|
||||||
|
Person{Name: "Alice", Age: 30},
|
||||||
|
&Person{Name: "Bob", Age: 25},
|
||||||
|
Employee{Person: Person{Name: "Charlie", Age: 35}, Position: "Developer"},
|
||||||
|
[]Person{{Name: "David", Age: 28}},
|
||||||
|
make(chan int),
|
||||||
|
func() { fmt.Println("函数") },
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, value := range complexValues {
|
||||||
|
fmt.Printf(" 复杂值 %d: ", i+1)
|
||||||
|
processComplexValue(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 类型 switch 的实际应用
|
||||||
|
fmt.Printf(" 类型 switch 的实际应用:\n")
|
||||||
|
|
||||||
|
// JSON 解析模拟
|
||||||
|
jsonData := map[string]interface{}{
|
||||||
|
"name": "Alice",
|
||||||
|
"age": 30,
|
||||||
|
"salary": 75000.50,
|
||||||
|
"active": true,
|
||||||
|
"skills": []interface{}{"Go", "Python", "JavaScript"},
|
||||||
|
"address": map[string]interface{}{
|
||||||
|
"city": "New York",
|
||||||
|
"zipcode": "10001",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf(" JSON 数据处理:\n")
|
||||||
|
processJSONData(jsonData, "")
|
||||||
|
|
||||||
|
fmt.Println()
|
||||||
|
}
|
||||||
|
|
||||||
|
// demonstratePracticalUsage 演示空接口的实际应用
|
||||||
|
func demonstratePracticalUsage() {
|
||||||
|
fmt.Println("4. 空接口的实际应用:")
|
||||||
|
|
||||||
|
// 应用1: 通用容器
|
||||||
|
fmt.Printf(" 应用1 - 通用容器:\n")
|
||||||
|
|
||||||
|
container := NewContainer()
|
||||||
|
|
||||||
|
// 存储不同类型的数据
|
||||||
|
container.Add("字符串数据")
|
||||||
|
container.Add(42)
|
||||||
|
container.Add(3.14)
|
||||||
|
container.Add([]int{1, 2, 3})
|
||||||
|
|
||||||
|
fmt.Printf(" 容器大小: %d\n", container.Size())
|
||||||
|
fmt.Printf(" 容器内容:\n")
|
||||||
|
for i := 0; i < container.Size(); i++ {
|
||||||
|
item := container.Get(i)
|
||||||
|
fmt.Printf(" [%d]: %v (%T)\n", i, item, item)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 应用2: 配置系统
|
||||||
|
fmt.Printf(" 应用2 - 配置系统:\n")
|
||||||
|
|
||||||
|
config := NewConfig()
|
||||||
|
|
||||||
|
// 设置不同类型的配置
|
||||||
|
config.Set("app_name", "MyApp")
|
||||||
|
config.Set("port", 8080)
|
||||||
|
config.Set("debug", true)
|
||||||
|
config.Set("timeout", 30.5)
|
||||||
|
config.Set("features", []string{"auth", "logging", "metrics"})
|
||||||
|
|
||||||
|
// 获取配置
|
||||||
|
fmt.Printf(" 应用名称: %v\n", config.Get("app_name"))
|
||||||
|
fmt.Printf(" 端口: %v\n", config.Get("port"))
|
||||||
|
fmt.Printf(" 调试模式: %v\n", config.Get("debug"))
|
||||||
|
fmt.Printf(" 超时时间: %v\n", config.Get("timeout"))
|
||||||
|
fmt.Printf(" 功能列表: %v\n", config.Get("features"))
|
||||||
|
|
||||||
|
// 类型安全的获取
|
||||||
|
if port, ok := config.GetInt("port"); ok {
|
||||||
|
fmt.Printf(" 端口 (类型安全): %d\n", port)
|
||||||
|
}
|
||||||
|
|
||||||
|
if debug, ok := config.GetBool("debug"); ok {
|
||||||
|
fmt.Printf(" 调试模式 (类型安全): %t\n", debug)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 应用3: 事件系统
|
||||||
|
fmt.Printf(" 应用3 - 事件系统:\n")
|
||||||
|
|
||||||
|
eventBus := NewEventBus()
|
||||||
|
|
||||||
|
// 注册事件处理器
|
||||||
|
eventBus.Subscribe("user_login", func(data interface{}) {
|
||||||
|
if user, ok := data.(map[string]interface{}); ok {
|
||||||
|
fmt.Printf(" 用户登录: %s\n", user["username"])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
eventBus.Subscribe("order_created", func(data interface{}) {
|
||||||
|
if order, ok := data.(map[string]interface{}); ok {
|
||||||
|
fmt.Printf(" 订单创建: ID=%v, 金额=$%.2f\n",
|
||||||
|
order["id"], order["amount"])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 发布事件
|
||||||
|
eventBus.Publish("user_login", map[string]interface{}{
|
||||||
|
"username": "alice",
|
||||||
|
"ip": "192.168.1.100",
|
||||||
|
})
|
||||||
|
|
||||||
|
eventBus.Publish("order_created", map[string]interface{}{
|
||||||
|
"id": 12345,
|
||||||
|
"amount": 99.99,
|
||||||
|
"items": []string{"laptop", "mouse"},
|
||||||
|
})
|
||||||
|
|
||||||
|
// 应用4: 数据转换
|
||||||
|
fmt.Printf(" 应用4 - 数据转换:\n")
|
||||||
|
|
||||||
|
converter := DataConverter{}
|
||||||
|
|
||||||
|
// 转换不同类型的数据
|
||||||
|
testData := []interface{}{
|
||||||
|
42,
|
||||||
|
"123",
|
||||||
|
3.14,
|
||||||
|
true,
|
||||||
|
"false",
|
||||||
|
[]int{1, 2, 3},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, data := range testData {
|
||||||
|
result := converter.ToString(data)
|
||||||
|
fmt.Printf(" %v (%T) -> \"%s\"\n", data, data, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println()
|
||||||
|
}
|
||||||
|
|
||||||
|
// demonstrateAdvancedUsage 演示空接口的高级用法
|
||||||
|
func demonstrateAdvancedUsage() {
|
||||||
|
fmt.Println("5. 空接口的高级用法:")
|
||||||
|
|
||||||
|
// 高级用法1: 反射结合空接口
|
||||||
|
fmt.Printf(" 高级用法1 - 反射结合空接口:\n")
|
||||||
|
|
||||||
|
inspector := TypeInspector{}
|
||||||
|
|
||||||
|
values := []interface{}{
|
||||||
|
42,
|
||||||
|
"Hello",
|
||||||
|
[]int{1, 2, 3},
|
||||||
|
map[string]int{"a": 1},
|
||||||
|
Person{Name: "Alice", Age: 30},
|
||||||
|
&Person{Name: "Bob", Age: 25},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, value := range values {
|
||||||
|
inspector.Inspect(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 高级用法2: 深拷贝
|
||||||
|
fmt.Printf(" 高级用法2 - 深拷贝:\n")
|
||||||
|
|
||||||
|
original := map[string]interface{}{
|
||||||
|
"name": "Alice",
|
||||||
|
"age": 30,
|
||||||
|
"address": map[string]interface{}{
|
||||||
|
"city": "New York",
|
||||||
|
"zip": "10001",
|
||||||
|
},
|
||||||
|
"hobbies": []interface{}{"reading", "swimming"},
|
||||||
|
}
|
||||||
|
|
||||||
|
copied := deepCopy(original)
|
||||||
|
|
||||||
|
fmt.Printf(" 原始数据: %v\n", original)
|
||||||
|
fmt.Printf(" 拷贝数据: %v\n", copied)
|
||||||
|
|
||||||
|
// 修改拷贝数据
|
||||||
|
if copiedMap, ok := copied.(map[string]interface{}); ok {
|
||||||
|
copiedMap["name"] = "Bob"
|
||||||
|
if address, ok := copiedMap["address"].(map[string]interface{}); ok {
|
||||||
|
address["city"] = "Boston"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf(" 修改后原始: %v\n", original)
|
||||||
|
fmt.Printf(" 修改后拷贝: %v\n", copied)
|
||||||
|
|
||||||
|
// 高级用法3: 序列化和反序列化
|
||||||
|
fmt.Printf(" 高级用法3 - 序列化和反序列化:\n")
|
||||||
|
|
||||||
|
serializer := SimpleSerializer{}
|
||||||
|
|
||||||
|
data := map[string]interface{}{
|
||||||
|
"user_id": 123,
|
||||||
|
"name": "Alice",
|
||||||
|
"active": true,
|
||||||
|
"score": 95.5,
|
||||||
|
}
|
||||||
|
|
||||||
|
// 序列化
|
||||||
|
serialized := serializer.Serialize(data)
|
||||||
|
fmt.Printf(" 序列化结果: %s\n", serialized)
|
||||||
|
|
||||||
|
// 反序列化
|
||||||
|
deserialized := serializer.Deserialize(serialized)
|
||||||
|
fmt.Printf(" 反序列化结果: %v\n", deserialized)
|
||||||
|
|
||||||
|
fmt.Println()
|
||||||
|
}
|
||||||
|
|
||||||
|
// demonstratePerformanceConsiderations 演示空接口的性能考虑
|
||||||
|
func demonstratePerformanceConsiderations() {
|
||||||
|
fmt.Println("6. 空接口的性能考虑:")
|
||||||
|
|
||||||
|
fmt.Printf(" 空接口的性能影响:\n")
|
||||||
|
fmt.Printf(" - 类型信息存储开销\n")
|
||||||
|
fmt.Printf(" - 装箱和拆箱成本\n")
|
||||||
|
fmt.Printf(" - 类型断言的运行时检查\n")
|
||||||
|
fmt.Printf(" - 垃圾回收压力增加\n")
|
||||||
|
|
||||||
|
// 性能对比示例
|
||||||
|
fmt.Printf(" 性能对比示例:\n")
|
||||||
|
|
||||||
|
// 直接类型操作
|
||||||
|
var directSum int
|
||||||
|
for i := 0; i < 1000; i++ {
|
||||||
|
directSum += i
|
||||||
|
}
|
||||||
|
fmt.Printf(" 直接类型操作结果: %d\n", directSum)
|
||||||
|
|
||||||
|
// 空接口操作
|
||||||
|
var interfaceSum int
|
||||||
|
var values []interface{}
|
||||||
|
for i := 0; i < 1000; i++ {
|
||||||
|
values = append(values, i)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, v := range values {
|
||||||
|
if num, ok := v.(int); ok {
|
||||||
|
interfaceSum += num
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fmt.Printf(" 空接口操作结果: %d\n", interfaceSum)
|
||||||
|
|
||||||
|
// 内存使用对比
|
||||||
|
fmt.Printf(" 内存使用对比:\n")
|
||||||
|
|
||||||
|
// 直接存储
|
||||||
|
directSlice := make([]int, 1000)
|
||||||
|
for i := range directSlice {
|
||||||
|
directSlice[i] = i
|
||||||
|
}
|
||||||
|
|
||||||
|
// 接口存储
|
||||||
|
interfaceSlice := make([]interface{}, 1000)
|
||||||
|
for i := range interfaceSlice {
|
||||||
|
interfaceSlice[i] = i
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf(" 直接存储切片长度: %d\n", len(directSlice))
|
||||||
|
fmt.Printf(" 接口存储切片长度: %d\n", len(interfaceSlice))
|
||||||
|
fmt.Printf(" 注意: 接口存储会有额外的内存开销\n")
|
||||||
|
|
||||||
|
fmt.Println()
|
||||||
|
}
|
||||||
|
|
||||||
|
// demonstrateBestPractices 演示空接口的最佳实践
|
||||||
|
func demonstrateBestPractices() {
|
||||||
|
fmt.Println("7. 空接口的最佳实践:")
|
||||||
|
|
||||||
|
fmt.Printf(" 最佳实践原则:\n")
|
||||||
|
fmt.Printf(" 1. 避免过度使用空接口\n")
|
||||||
|
fmt.Printf(" 2. 优先使用具体类型或定义的接口\n")
|
||||||
|
fmt.Printf(" 3. 使用类型断言时要检查成功与否\n")
|
||||||
|
fmt.Printf(" 4. 考虑使用泛型替代空接口(Go 1.18+)\n")
|
||||||
|
fmt.Printf(" 5. 在 API 边界谨慎使用空接口\n")
|
||||||
|
|
||||||
|
// 好的实践示例
|
||||||
|
fmt.Printf(" 好的实践示例:\n")
|
||||||
|
|
||||||
|
// 1. 明确的接口定义
|
||||||
|
fmt.Printf(" 1. 使用明确的接口而不是空接口:\n")
|
||||||
|
|
||||||
|
var printer Printer = ConsolePrinter{}
|
||||||
|
printer.Print("使用明确接口的消息")
|
||||||
|
|
||||||
|
// 2. 类型安全的容器
|
||||||
|
fmt.Printf(" 2. 类型安全的容器:\n")
|
||||||
|
|
||||||
|
stringContainer := NewTypedContainer[string]()
|
||||||
|
stringContainer.Add("Hello")
|
||||||
|
stringContainer.Add("World")
|
||||||
|
|
||||||
|
fmt.Printf(" 字符串容器: %v\n", stringContainer.GetAll())
|
||||||
|
|
||||||
|
intContainer := NewTypedContainer[int]()
|
||||||
|
intContainer.Add(1)
|
||||||
|
intContainer.Add(2)
|
||||||
|
intContainer.Add(3)
|
||||||
|
|
||||||
|
fmt.Printf(" 整数容器: %v\n", intContainer.GetAll())
|
||||||
|
|
||||||
|
// 3. 错误处理
|
||||||
|
fmt.Printf(" 3. 正确的错误处理:\n")
|
||||||
|
|
||||||
|
result, err := safeTypeAssertion("Hello, World!", "string")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf(" 类型断言错误: %v\n", err)
|
||||||
|
} else {
|
||||||
|
fmt.Printf(" 类型断言成功: %v\n", result)
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err = safeTypeAssertion(42, "string")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf(" 类型断言错误: %v\n", err)
|
||||||
|
} else {
|
||||||
|
fmt.Printf(" 类型断言成功: %v\n", result)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 避免的反模式
|
||||||
|
fmt.Printf(" 避免的反模式:\n")
|
||||||
|
fmt.Printf(" - 函数参数和返回值过度使用 interface{}\n")
|
||||||
|
fmt.Printf(" - 不检查类型断言的成功与否\n")
|
||||||
|
fmt.Printf(" - 在性能敏感的代码中大量使用空接口\n")
|
||||||
|
fmt.Printf(" - 用空接口替代适当的类型设计\n")
|
||||||
|
|
||||||
|
fmt.Println()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========== 类型定义 ==========
|
||||||
|
|
||||||
|
// 基本结构体
|
||||||
|
type Person struct {
|
||||||
|
Name string
|
||||||
|
Age int
|
||||||
|
}
|
||||||
|
|
||||||
|
type Employee struct {
|
||||||
|
Person
|
||||||
|
Position string
|
||||||
|
}
|
||||||
|
|
||||||
|
// 打印器接口
|
||||||
|
type Printer interface {
|
||||||
|
Print(message string)
|
||||||
|
}
|
||||||
|
|
||||||
|
type ConsolePrinter struct{}
|
||||||
|
|
||||||
|
func (cp ConsolePrinter) Print(message string) {
|
||||||
|
fmt.Printf(" [控制台] %s\n", message)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========== 实用工具类型 ==========
|
||||||
|
|
||||||
|
// 通用容器
|
||||||
|
type Container struct {
|
||||||
|
items []interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewContainer() *Container {
|
||||||
|
return &Container{items: make([]interface{}, 0)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Container) Add(item interface{}) {
|
||||||
|
c.items = append(c.items, item)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Container) Get(index int) interface{} {
|
||||||
|
if index >= 0 && index < len(c.items) {
|
||||||
|
return c.items[index]
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Container) Size() int {
|
||||||
|
return len(c.items)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 配置系统
|
||||||
|
type Config struct {
|
||||||
|
data map[string]interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewConfig() *Config {
|
||||||
|
return &Config{data: make(map[string]interface{})}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Config) Set(key string, value interface{}) {
|
||||||
|
c.data[key] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Config) Get(key string) interface{} {
|
||||||
|
return c.data[key]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Config) GetString(key string) (string, bool) {
|
||||||
|
if value, exists := c.data[key]; exists {
|
||||||
|
if str, ok := value.(string); ok {
|
||||||
|
return str, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Config) GetInt(key string) (int, bool) {
|
||||||
|
if value, exists := c.data[key]; exists {
|
||||||
|
if num, ok := value.(int); ok {
|
||||||
|
return num, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Config) GetBool(key string) (bool, bool) {
|
||||||
|
if value, exists := c.data[key]; exists {
|
||||||
|
if b, ok := value.(bool); ok {
|
||||||
|
return b, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false, false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 事件系统
|
||||||
|
type EventBus struct {
|
||||||
|
handlers map[string][]func(interface{})
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewEventBus() *EventBus {
|
||||||
|
return &EventBus{handlers: make(map[string][]func(interface{}))}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (eb *EventBus) Subscribe(event string, handler func(interface{})) {
|
||||||
|
eb.handlers[event] = append(eb.handlers[event], handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (eb *EventBus) Publish(event string, data interface{}) {
|
||||||
|
if handlers, exists := eb.handlers[event]; exists {
|
||||||
|
for _, handler := range handlers {
|
||||||
|
handler(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 数据转换器
|
||||||
|
type DataConverter struct{}
|
||||||
|
|
||||||
|
func (dc DataConverter) ToString(value interface{}) string {
|
||||||
|
switch v := value.(type) {
|
||||||
|
case string:
|
||||||
|
return v
|
||||||
|
case int:
|
||||||
|
return strconv.Itoa(v)
|
||||||
|
case float64:
|
||||||
|
return strconv.FormatFloat(v, 'f', -1, 64)
|
||||||
|
case bool:
|
||||||
|
return strconv.FormatBool(v)
|
||||||
|
case []int:
|
||||||
|
result := "["
|
||||||
|
for i, num := range v {
|
||||||
|
if i > 0 {
|
||||||
|
result += ", "
|
||||||
|
}
|
||||||
|
result += strconv.Itoa(num)
|
||||||
|
}
|
||||||
|
result += "]"
|
||||||
|
return result
|
||||||
|
default:
|
||||||
|
return fmt.Sprintf("%v", v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 类型检查器
|
||||||
|
type TypeInspector struct{}
|
||||||
|
|
||||||
|
func (ti TypeInspector) Inspect(value interface{}) {
|
||||||
|
if value == nil {
|
||||||
|
fmt.Printf(" nil 值\n")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
t := reflect.TypeOf(value)
|
||||||
|
v := reflect.ValueOf(value)
|
||||||
|
|
||||||
|
fmt.Printf(" 类型: %s, 种类: %s, 值: %v\n", t.String(), t.Kind().String(), value)
|
||||||
|
|
||||||
|
switch t.Kind() {
|
||||||
|
case reflect.Struct:
|
||||||
|
fmt.Printf(" 结构体字段数: %d\n", t.NumField())
|
||||||
|
for i := 0; i < t.NumField(); i++ {
|
||||||
|
field := t.Field(i)
|
||||||
|
fieldValue := v.Field(i)
|
||||||
|
fmt.Printf(" %s: %v (%s)\n", field.Name, fieldValue.Interface(), field.Type)
|
||||||
|
}
|
||||||
|
case reflect.Ptr:
|
||||||
|
fmt.Printf(" 指针指向: %s\n", t.Elem().String())
|
||||||
|
if !v.IsNil() {
|
||||||
|
fmt.Printf(" 指针值: %v\n", v.Elem().Interface())
|
||||||
|
}
|
||||||
|
case reflect.Slice:
|
||||||
|
fmt.Printf(" 切片长度: %d, 容量: %d\n", v.Len(), v.Cap())
|
||||||
|
fmt.Printf(" 元素类型: %s\n", t.Elem().String())
|
||||||
|
case reflect.Map:
|
||||||
|
fmt.Printf(" 映射长度: %d\n", v.Len())
|
||||||
|
fmt.Printf(" 键类型: %s, 值类型: %s\n", t.Key().String(), t.Elem().String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 简单序列化器
|
||||||
|
type SimpleSerializer struct{}
|
||||||
|
|
||||||
|
func (ss SimpleSerializer) Serialize(data interface{}) string {
|
||||||
|
switch v := data.(type) {
|
||||||
|
case map[string]interface{}:
|
||||||
|
result := "{"
|
||||||
|
first := true
|
||||||
|
for key, value := range v {
|
||||||
|
if !first {
|
||||||
|
result += ", "
|
||||||
|
}
|
||||||
|
result += fmt.Sprintf("\"%s\": %s", key, ss.serializeValue(value))
|
||||||
|
first = false
|
||||||
|
}
|
||||||
|
result += "}"
|
||||||
|
return result
|
||||||
|
default:
|
||||||
|
return ss.serializeValue(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ss SimpleSerializer) serializeValue(value interface{}) string {
|
||||||
|
switch v := value.(type) {
|
||||||
|
case string:
|
||||||
|
return fmt.Sprintf("\"%s\"", v)
|
||||||
|
case int:
|
||||||
|
return strconv.Itoa(v)
|
||||||
|
case float64:
|
||||||
|
return strconv.FormatFloat(v, 'f', -1, 64)
|
||||||
|
case bool:
|
||||||
|
return strconv.FormatBool(v)
|
||||||
|
default:
|
||||||
|
return fmt.Sprintf("\"%v\"", v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ss SimpleSerializer) Deserialize(data string) interface{} {
|
||||||
|
// 简化的反序列化,实际应用中应该使用 JSON 包
|
||||||
|
if data == "true" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if data == "false" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if num, err := strconv.Atoi(data); err == nil {
|
||||||
|
return num
|
||||||
|
}
|
||||||
|
if num, err := strconv.ParseFloat(data, 64); err == nil {
|
||||||
|
return num
|
||||||
|
}
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
// 类型安全的泛型容器(Go 1.18+)
|
||||||
|
type TypedContainer[T any] struct {
|
||||||
|
items []T
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTypedContainer[T any]() *TypedContainer[T] {
|
||||||
|
return &TypedContainer[T]{items: make([]T, 0)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tc *TypedContainer[T]) Add(item T) {
|
||||||
|
tc.items = append(tc.items, item)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tc *TypedContainer[T]) Get(index int) T {
|
||||||
|
var zero T
|
||||||
|
if index >= 0 && index < len(tc.items) {
|
||||||
|
return tc.items[index]
|
||||||
|
}
|
||||||
|
return zero
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tc *TypedContainer[T]) GetAll() []T {
|
||||||
|
return tc.items
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========== 辅助函数 ==========
|
||||||
|
|
||||||
|
// 处理值的类型 switch
|
||||||
|
func processValue(value interface{}) {
|
||||||
|
switch v := value.(type) {
|
||||||
|
case nil:
|
||||||
|
fmt.Printf("nil 值\n")
|
||||||
|
case int:
|
||||||
|
fmt.Printf("整数: %d\n", v)
|
||||||
|
case string:
|
||||||
|
fmt.Printf("字符串: \"%s\" (长度: %d)\n", v, len(v))
|
||||||
|
case float64:
|
||||||
|
fmt.Printf("浮点数: %.3f\n", v)
|
||||||
|
case bool:
|
||||||
|
fmt.Printf("布尔值: %t\n", v)
|
||||||
|
case []int:
|
||||||
|
fmt.Printf("整数切片: %v (长度: %d)\n", v, len(v))
|
||||||
|
case map[string]int:
|
||||||
|
fmt.Printf("字符串到整数的映射: %v (大小: %d)\n", v, len(v))
|
||||||
|
default:
|
||||||
|
fmt.Printf("未知类型: %T, 值: %v\n", v, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理复杂值的类型 switch
|
||||||
|
func processComplexValue(value interface{}) {
|
||||||
|
switch v := value.(type) {
|
||||||
|
case Person:
|
||||||
|
fmt.Printf("Person 结构体: %s (%d岁)\n", v.Name, v.Age)
|
||||||
|
case *Person:
|
||||||
|
fmt.Printf("Person 指针: %s (%d岁)\n", v.Name, v.Age)
|
||||||
|
case Employee:
|
||||||
|
fmt.Printf("Employee 结构体: %s - %s\n", v.Name, v.Position)
|
||||||
|
case []Person:
|
||||||
|
fmt.Printf("Person 切片: %d 个人\n", len(v))
|
||||||
|
case chan int:
|
||||||
|
fmt.Printf("整数通道\n")
|
||||||
|
case func():
|
||||||
|
fmt.Printf("无参数函数\n")
|
||||||
|
default:
|
||||||
|
fmt.Printf("其他复杂类型: %T\n", v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理 JSON 数据
|
||||||
|
func processJSONData(data map[string]interface{}, indent string) {
|
||||||
|
for key, value := range data {
|
||||||
|
fmt.Printf("%s %s: ", indent, key)
|
||||||
|
|
||||||
|
switch v := value.(type) {
|
||||||
|
case string:
|
||||||
|
fmt.Printf("\"%s\" (字符串)\n", v)
|
||||||
|
case float64:
|
||||||
|
fmt.Printf("%.2f (数字)\n", v)
|
||||||
|
case bool:
|
||||||
|
fmt.Printf("%t (布尔值)\n", v)
|
||||||
|
case []interface{}:
|
||||||
|
fmt.Printf("数组 [%d 个元素]\n", len(v))
|
||||||
|
for i, item := range v {
|
||||||
|
fmt.Printf("%s [%d]: %v (%T)\n", indent, i, item, item)
|
||||||
|
}
|
||||||
|
case map[string]interface{}:
|
||||||
|
fmt.Printf("对象 {%d 个字段}\n", len(v))
|
||||||
|
processJSONData(v, indent+" ")
|
||||||
|
default:
|
||||||
|
fmt.Printf("%v (%T)\n", v, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 深拷贝函数
|
||||||
|
func deepCopy(original interface{}) interface{} {
|
||||||
|
switch v := original.(type) {
|
||||||
|
case map[string]interface{}:
|
||||||
|
copied := make(map[string]interface{})
|
||||||
|
for key, value := range v {
|
||||||
|
copied[key] = deepCopy(value)
|
||||||
|
}
|
||||||
|
return copied
|
||||||
|
case []interface{}:
|
||||||
|
copied := make([]interface{}, len(v))
|
||||||
|
for i, value := range v {
|
||||||
|
copied[i] = deepCopy(value)
|
||||||
|
}
|
||||||
|
return copied
|
||||||
|
default:
|
||||||
|
// 基本类型直接返回(值拷贝)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 安全的类型断言
|
||||||
|
func safeTypeAssertion(value interface{}, expectedType string) (interface{}, error) {
|
||||||
|
switch expectedType {
|
||||||
|
case "string":
|
||||||
|
if str, ok := value.(string); ok {
|
||||||
|
return str, nil
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("期望 string 类型,实际是 %T", value)
|
||||||
|
case "int":
|
||||||
|
if num, ok := value.(int); ok {
|
||||||
|
return num, nil
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("期望 int 类型,实际是 %T", value)
|
||||||
|
case "bool":
|
||||||
|
if b, ok := value.(bool); ok {
|
||||||
|
return b, nil
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("期望 bool 类型,实际是 %T", value)
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("不支持的类型: %s", expectedType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
运行这个程序:
|
||||||
|
go run 02-empty-interface.go
|
||||||
|
|
||||||
|
学习要点:
|
||||||
|
1. 空接口 interface{} 可以持有任何类型的值
|
||||||
|
2. 空接口是 Go 语言中最通用的类型
|
||||||
|
3. 使用类型断言从空接口中提取具体类型的值
|
||||||
|
4. 类型 switch 是处理空接口的优雅方式
|
||||||
|
5. 空接口在泛型编程和动态类型处理中很有用
|
||||||
|
|
||||||
|
空接口的特性:
|
||||||
|
1. 通用性:可以存储任何类型的值
|
||||||
|
2. 类型安全:通过类型断言保证类型安全
|
||||||
|
3. 运行时类型信息:保存值的动态类型信息
|
||||||
|
4. 零值:空接口的零值是 nil
|
||||||
|
5. 性能开销:有装箱拆箱的成本
|
||||||
|
|
||||||
|
类型断言:
|
||||||
|
1. 安全断言:value.(Type) 返回值和布尔标志
|
||||||
|
2. 不安全断言:value.(Type) 只返回值,失败时 panic
|
||||||
|
3. 类型 switch:switch value.(type) 处理多种类型
|
||||||
|
4. 类型检查:使用 ok 模式检查断言是否成功
|
||||||
|
|
||||||
|
常见应用场景:
|
||||||
|
1. 通用容器和集合
|
||||||
|
2. 配置系统和参数传递
|
||||||
|
3. JSON 和其他格式的数据处理
|
||||||
|
4. 事件系统和消息传递
|
||||||
|
5. 插件系统和动态加载
|
||||||
|
6. 序列化和反序列化
|
||||||
|
7. 反射和元编程
|
||||||
|
|
||||||
|
性能考虑:
|
||||||
|
1. 装箱开销:基本类型存储到接口需要装箱
|
||||||
|
2. 类型断言成本:运行时类型检查有开销
|
||||||
|
3. 内存使用:接口存储需要额外的类型信息
|
||||||
|
4. 垃圾回收:可能增加 GC 压力
|
||||||
|
|
||||||
|
最佳实践:
|
||||||
|
1. 避免过度使用空接口
|
||||||
|
2. 优先使用具体类型或定义的接口
|
||||||
|
3. 使用类型断言时检查成功与否
|
||||||
|
4. 考虑使用泛型替代空接口(Go 1.18+)
|
||||||
|
5. 在 API 设计中谨慎使用空接口
|
||||||
|
6. 提供类型安全的包装函数
|
||||||
|
|
||||||
|
何时使用空接口:
|
||||||
|
1. 需要存储不同类型的值
|
||||||
|
2. 处理动态类型的数据(如 JSON)
|
||||||
|
3. 实现通用的算法或容器
|
||||||
|
4. 与反射结合使用
|
||||||
|
5. 临时的类型转换需求
|
||||||
|
|
||||||
|
何时避免空接口:
|
||||||
|
1. 类型在编译时已知
|
||||||
|
2. 性能敏感的代码
|
||||||
|
3. 可以用泛型替代的场景
|
||||||
|
4. API 的公共接口设计
|
||||||
|
5. 简单的数据传递
|
||||||
|
|
||||||
|
注意事项:
|
||||||
|
1. nil 接口不能调用方法
|
||||||
|
2. 类型断言失败可能导致 panic
|
||||||
|
3. 空接口比较需要注意动态类型
|
||||||
|
4. 过度使用会降低代码的类型安全性
|
||||||
|
5. 调试时类型信息可能不够清晰
|
||||||
|
*/
|
1058
golang-learning/05-interfaces/03-type-assertions.go
Normal file
1058
golang-learning/05-interfaces/03-type-assertions.go
Normal file
File diff suppressed because it is too large
Load Diff
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
957
golang-learning/07-error-handling/01-basic-errors.go
Normal file
957
golang-learning/07-error-handling/01-basic-errors.go
Normal file
@@ -0,0 +1,957 @@
|
|||||||
|
/*
|
||||||
|
01-basic-errors.go - Go 语言基础错误处理详解
|
||||||
|
|
||||||
|
学习目标:
|
||||||
|
1. 理解 Go 语言的错误处理哲学
|
||||||
|
2. 掌握 error 接口的使用
|
||||||
|
3. 学会基本的错误处理模式
|
||||||
|
4. 了解错误传播和包装
|
||||||
|
5. 掌握错误处理的最佳实践
|
||||||
|
|
||||||
|
知识点:
|
||||||
|
- error 接口的定义和实现
|
||||||
|
- 错误的创建和返回
|
||||||
|
- 错误检查和处理
|
||||||
|
- 错误传播和包装
|
||||||
|
- 错误处理的惯用法
|
||||||
|
- 错误信息的设计
|
||||||
|
*/
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
fmt.Println("=== Go 语言基础错误处理详解 ===\\n")
|
||||||
|
|
||||||
|
// 演示错误接口的基本概念
|
||||||
|
demonstrateErrorInterface()
|
||||||
|
|
||||||
|
// 演示错误的创建方式
|
||||||
|
demonstrateErrorCreation()
|
||||||
|
|
||||||
|
// 演示错误检查和处理
|
||||||
|
demonstrateErrorHandling()
|
||||||
|
|
||||||
|
// 演示错误传播
|
||||||
|
demonstrateErrorPropagation()
|
||||||
|
|
||||||
|
// 演示错误包装
|
||||||
|
demonstrateErrorWrapping()
|
||||||
|
|
||||||
|
// 演示错误处理模式
|
||||||
|
demonstrateErrorPatterns()
|
||||||
|
|
||||||
|
// 演示实际应用场景
|
||||||
|
demonstratePracticalExamples()
|
||||||
|
}
|
||||||
|
|
||||||
|
// demonstrateErrorInterface 演示错误接口的基本概念
|
||||||
|
func demonstrateErrorInterface() {
|
||||||
|
fmt.Println("1. 错误接口的基本概念:")
|
||||||
|
|
||||||
|
// error 接口的定义
|
||||||
|
fmt.Printf(" error 接口的定义:\\n")
|
||||||
|
fmt.Printf(" type error interface {\\n")
|
||||||
|
fmt.Printf(" Error() string\\n")
|
||||||
|
fmt.Printf(" }\\n")
|
||||||
|
fmt.Printf("\\n")
|
||||||
|
fmt.Printf(" 错误处理的特点:\\n")
|
||||||
|
fmt.Printf(" - 错误是值,可以像其他值一样传递\\n")
|
||||||
|
fmt.Printf(" - 使用多返回值模式处理错误\\n")
|
||||||
|
fmt.Printf(" - 显式错误检查,不隐藏错误\\n")
|
||||||
|
fmt.Printf(" - 错误应该被处理,不应该被忽略\\n")
|
||||||
|
fmt.Printf(" - 错误信息应该清晰、有用\\n")
|
||||||
|
|
||||||
|
// 基本错误示例
|
||||||
|
fmt.Printf(" 基本错误示例:\\n")
|
||||||
|
|
||||||
|
// 成功的情况
|
||||||
|
result, err := divide(10, 2)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf(" 错误: %v\\n", err)
|
||||||
|
} else {
|
||||||
|
fmt.Printf(" 10 / 2 = %.2f\\n", result)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 错误的情况
|
||||||
|
result, err = divide(10, 0)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf(" 错误: %v\\n", err)
|
||||||
|
} else {
|
||||||
|
fmt.Printf(" 10 / 0 = %.2f\\n", result)
|
||||||
|
}
|
||||||
|
|
||||||
|
// nil 错误检查
|
||||||
|
fmt.Printf(" nil 错误检查:\\n")
|
||||||
|
|
||||||
|
var nilError error
|
||||||
|
fmt.Printf(" nil 错误: %v\\n", nilError)
|
||||||
|
fmt.Printf(" nil 错误 == nil: %t\\n", nilError == nil)
|
||||||
|
|
||||||
|
// 错误比较
|
||||||
|
fmt.Printf(" 错误比较:\\n")
|
||||||
|
|
||||||
|
err1 := errors.New("相同的错误")
|
||||||
|
err2 := errors.New("相同的错误")
|
||||||
|
fmt.Printf(" err1 == err2: %t (内容相同但不是同一个对象)\\n", err1 == err2)
|
||||||
|
fmt.Printf(" err1.Error() == err2.Error(): %t\\n", err1.Error() == err2.Error())
|
||||||
|
|
||||||
|
fmt.Println()
|
||||||
|
}
|
||||||
|
|
||||||
|
// demonstrateErrorCreation 演示错误的创建方式
|
||||||
|
func demonstrateErrorCreation() {
|
||||||
|
fmt.Println("2. 错误的创建方式:")
|
||||||
|
|
||||||
|
// 使用 errors.New 创建错误
|
||||||
|
fmt.Printf(" 使用 errors.New 创建错误:\\n")
|
||||||
|
|
||||||
|
err1 := errors.New("这是一个简单的错误")
|
||||||
|
fmt.Printf(" 错误1: %v\\n", err1)
|
||||||
|
|
||||||
|
// 使用 fmt.Errorf 创建格式化错误
|
||||||
|
fmt.Printf(" 使用 fmt.Errorf 创建格式化错误:\\n")
|
||||||
|
|
||||||
|
username := "alice"
|
||||||
|
err2 := fmt.Errorf("用户 %s 不存在", username)
|
||||||
|
fmt.Printf(" 错误2: %v\\n", err2)
|
||||||
|
|
||||||
|
// 创建带有更多信息的错误
|
||||||
|
age := -5
|
||||||
|
err3 := fmt.Errorf("无效的年龄值: %d (年龄必须大于0)", age)
|
||||||
|
fmt.Printf(" 错误3: %v\\n", err3)
|
||||||
|
|
||||||
|
// 使用预定义错误
|
||||||
|
fmt.Printf(" 使用预定义错误:\\n")
|
||||||
|
|
||||||
|
predefinedErrors := []error{
|
||||||
|
ErrInvalidInput,
|
||||||
|
ErrNotFound,
|
||||||
|
ErrPermissionDenied,
|
||||||
|
ErrTimeout,
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, err := range predefinedErrors {
|
||||||
|
fmt.Printf(" 预定义错误%d: %v\\n", i+1, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 条件性错误创建
|
||||||
|
fmt.Printf(" 条件性错误创建:\\n")
|
||||||
|
|
||||||
|
testValues := []int{-1, 0, 5, 101}
|
||||||
|
for _, value := range testValues {
|
||||||
|
if err := validateAge(value); err != nil {
|
||||||
|
fmt.Printf(" 验证年龄 %d: %v\\n", value, err)
|
||||||
|
} else {
|
||||||
|
fmt.Printf(" 验证年龄 %d: 有效\\n", value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println()
|
||||||
|
}
|
||||||
|
|
||||||
|
// demonstrateErrorHandling 演示错误检查和处理
|
||||||
|
func demonstrateErrorHandling() {
|
||||||
|
fmt.Println("3. 错误检查和处理:")
|
||||||
|
|
||||||
|
// 基本错误检查模式
|
||||||
|
fmt.Printf(" 基本错误检查模式:\\n")
|
||||||
|
|
||||||
|
// 立即检查错误
|
||||||
|
content, err := readFile("example.txt")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf(" 读取文件失败: %v\\n", err)
|
||||||
|
} else {
|
||||||
|
fmt.Printf(" 文件内容: %s\\n", content)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 多个操作的错误处理
|
||||||
|
fmt.Printf(" 多个操作的错误处理:\\n")
|
||||||
|
|
||||||
|
err = performMultipleOperations()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf(" 多个操作失败: %v\\n", err)
|
||||||
|
} else {
|
||||||
|
fmt.Printf(" 所有操作成功完成\\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 错误类型检查
|
||||||
|
fmt.Printf(" 错误类型检查:\\n")
|
||||||
|
|
||||||
|
testOperations := []func() error{
|
||||||
|
func() error { return ErrNotFound },
|
||||||
|
func() error { return ErrPermissionDenied },
|
||||||
|
func() error { return fmt.Errorf("未知错误") },
|
||||||
|
func() error { return nil },
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, op := range testOperations {
|
||||||
|
err := op()
|
||||||
|
fmt.Printf(" 操作%d: ", i+1)
|
||||||
|
handleSpecificError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 错误恢复
|
||||||
|
fmt.Printf(" 错误恢复:\\n")
|
||||||
|
|
||||||
|
data, err := fetchDataWithRetry("https://api.example.com/data", 3)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf(" 获取数据失败: %v\\n", err)
|
||||||
|
} else {
|
||||||
|
fmt.Printf(" 获取数据成功: %s\\n", data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 部分失败处理
|
||||||
|
fmt.Printf(" 部分失败处理:\\n")
|
||||||
|
|
||||||
|
results, errors := processBatch([]string{"item1", "item2", "invalid", "item4"})
|
||||||
|
fmt.Printf(" 成功处理: %v\\n", results)
|
||||||
|
fmt.Printf(" 处理错误: %v\\n", errors)
|
||||||
|
|
||||||
|
fmt.Println()
|
||||||
|
}
|
||||||
|
|
||||||
|
// demonstrateErrorPropagation 演示错误传播
|
||||||
|
func demonstrateErrorPropagation() {
|
||||||
|
fmt.Println("4. 错误传播:")
|
||||||
|
|
||||||
|
// 简单错误传播
|
||||||
|
fmt.Printf(" 简单错误传播:\\n")
|
||||||
|
|
||||||
|
result, err := calculateTotal([]float64{10.5, 20.0, -5.0, 15.5})
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf(" 计算总和失败: %v\\n", err)
|
||||||
|
} else {
|
||||||
|
fmt.Printf(" 总和: %.2f\\n", result)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 带上下文的错误传播
|
||||||
|
fmt.Printf(" 带上下文的错误传播:\\n")
|
||||||
|
|
||||||
|
user, err := getUserProfile("nonexistent_user")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf(" 获取用户资料失败: %v\\n", err)
|
||||||
|
} else {
|
||||||
|
fmt.Printf(" 用户资料: %+v\\n", user)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 错误链
|
||||||
|
fmt.Printf(" 错误链:\\n")
|
||||||
|
|
||||||
|
err = processOrder("invalid_order_id")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf(" 处理订单失败: %v\\n", err)
|
||||||
|
|
||||||
|
// 展示错误链
|
||||||
|
fmt.Printf(" 错误链分析:\\n")
|
||||||
|
printErrorChain(err, " ")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 选择性错误传播
|
||||||
|
fmt.Printf(" 选择性错误传播:\\n")
|
||||||
|
|
||||||
|
results := []string{}
|
||||||
|
errors := []error{}
|
||||||
|
|
||||||
|
items := []string{"valid1", "invalid", "valid2", "error", "valid3"}
|
||||||
|
for _, item := range items {
|
||||||
|
result, err := processItem(item)
|
||||||
|
if err != nil {
|
||||||
|
errors = append(errors, fmt.Errorf("处理 %s 失败: %w", item, err))
|
||||||
|
} else {
|
||||||
|
results = append(results, result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf(" 成功结果: %v\\n", results)
|
||||||
|
if len(errors) > 0 {
|
||||||
|
fmt.Printf(" 错误列表:\\n")
|
||||||
|
for _, err := range errors {
|
||||||
|
fmt.Printf(" - %v\\n", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println()
|
||||||
|
}
|
||||||
|
|
||||||
|
// demonstrateErrorWrapping 演示错误包装
|
||||||
|
func demonstrateErrorWrapping() {
|
||||||
|
fmt.Println("5. 错误包装:")
|
||||||
|
|
||||||
|
// 基本错误包装
|
||||||
|
fmt.Printf(" 基本错误包装:\\n")
|
||||||
|
|
||||||
|
originalErr := errors.New("原始错误")
|
||||||
|
wrappedErr := fmt.Errorf("包装错误: %w", originalErr)
|
||||||
|
|
||||||
|
fmt.Printf(" 原始错误: %v\\n", originalErr)
|
||||||
|
fmt.Printf(" 包装错误: %v\\n", wrappedErr)
|
||||||
|
|
||||||
|
// 错误解包
|
||||||
|
fmt.Printf(" 错误解包:\\n")
|
||||||
|
|
||||||
|
unwrappedErr := errors.Unwrap(wrappedErr)
|
||||||
|
fmt.Printf(" 解包后的错误: %v\\n", unwrappedErr)
|
||||||
|
fmt.Printf(" 解包后是否为原始错误: %t\\n", unwrappedErr == originalErr)
|
||||||
|
|
||||||
|
// 错误检查 (errors.Is)
|
||||||
|
fmt.Printf(" 错误检查 (errors.Is):\\n")
|
||||||
|
|
||||||
|
fmt.Printf(" wrappedErr 是否为 originalErr: %t\\n", errors.Is(wrappedErr, originalErr))
|
||||||
|
fmt.Printf(" wrappedErr 是否为 ErrNotFound: %t\\n", errors.Is(wrappedErr, ErrNotFound))
|
||||||
|
|
||||||
|
// 多层包装
|
||||||
|
fmt.Printf(" 多层包装:\\n")
|
||||||
|
|
||||||
|
baseErr := ErrNotFound
|
||||||
|
level1Err := fmt.Errorf("数据库查询失败: %w", baseErr)
|
||||||
|
level2Err := fmt.Errorf("用户服务错误: %w", level1Err)
|
||||||
|
level3Err := fmt.Errorf("API 请求失败: %w", level2Err)
|
||||||
|
|
||||||
|
fmt.Printf(" 多层包装错误: %v\\n", level3Err)
|
||||||
|
fmt.Printf(" 是否包含 ErrNotFound: %t\\n", errors.Is(level3Err, ErrNotFound))
|
||||||
|
|
||||||
|
// 错误类型断言 (errors.As)
|
||||||
|
fmt.Printf(" 错误类型断言 (errors.As):\\n")
|
||||||
|
|
||||||
|
customErr := &CustomError{
|
||||||
|
Code: 404,
|
||||||
|
Message: "资源未找到",
|
||||||
|
Details: "请求的用户ID不存在",
|
||||||
|
}
|
||||||
|
|
||||||
|
wrappedCustomErr := fmt.Errorf("处理请求失败: %w", customErr)
|
||||||
|
|
||||||
|
var targetErr *CustomError
|
||||||
|
if errors.As(wrappedCustomErr, &targetErr) {
|
||||||
|
fmt.Printf(" 找到自定义错误: Code=%d, Message=%s\\n",
|
||||||
|
targetErr.Code, targetErr.Message)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println()
|
||||||
|
}
|
||||||
|
|
||||||
|
// demonstrateErrorPatterns 演示错误处理模式
|
||||||
|
func demonstrateErrorPatterns() {
|
||||||
|
fmt.Println("6. 错误处理模式:")
|
||||||
|
|
||||||
|
// 哨兵错误模式
|
||||||
|
fmt.Printf(" 哨兵错误模式:\\n")
|
||||||
|
|
||||||
|
_, err := findUser("nonexistent")
|
||||||
|
if errors.Is(err, ErrUserNotFound) {
|
||||||
|
fmt.Printf(" 用户不存在,使用默认用户\\n")
|
||||||
|
} else if err != nil {
|
||||||
|
fmt.Printf(" 查找用户时发生其他错误: %v\\n", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 错误类型模式
|
||||||
|
fmt.Printf(" 错误类型模式:\\n")
|
||||||
|
|
||||||
|
err = performNetworkOperation()
|
||||||
|
var netErr *NetworkError
|
||||||
|
if errors.As(err, &netErr) {
|
||||||
|
if netErr.Temporary {
|
||||||
|
fmt.Printf(" 临时网络错误,可以重试: %v\\n", netErr)
|
||||||
|
} else {
|
||||||
|
fmt.Printf(" 永久网络错误: %v\\n", netErr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 不透明错误模式
|
||||||
|
fmt.Printf(" 不透明错误模式:\\n")
|
||||||
|
|
||||||
|
err = callExternalAPI()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf(" 外部API调用失败: %v\\n", err)
|
||||||
|
// 不检查具体错误类型,统一处理
|
||||||
|
}
|
||||||
|
|
||||||
|
// 错误聚合模式
|
||||||
|
fmt.Printf(" 错误聚合模式:\\n")
|
||||||
|
|
||||||
|
validator := NewValidator()
|
||||||
|
validator.ValidateRequired("name", "")
|
||||||
|
validator.ValidateEmail("email", "invalid-email")
|
||||||
|
validator.ValidateRange("age", -5, 0, 120)
|
||||||
|
|
||||||
|
if validator.HasErrors() {
|
||||||
|
fmt.Printf(" 验证失败:\\n")
|
||||||
|
for _, err := range validator.Errors() {
|
||||||
|
fmt.Printf(" - %v\\n", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 错误恢复模式
|
||||||
|
fmt.Printf(" 错误恢复模式:\\n")
|
||||||
|
|
||||||
|
result := performWithFallback()
|
||||||
|
fmt.Printf(" 操作结果: %s\\n", result)
|
||||||
|
|
||||||
|
// 错误重试模式
|
||||||
|
fmt.Printf(" 错误重试模式:\\n")
|
||||||
|
|
||||||
|
data, err := retryOperation(func() (string, error) {
|
||||||
|
// 模拟不稳定的操作
|
||||||
|
if len(fmt.Sprintf("%d", rand.Int()))%3 == 0 {
|
||||||
|
return "成功数据", nil
|
||||||
|
}
|
||||||
|
return "", errors.New("临时失败")
|
||||||
|
}, 3)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf(" 重试操作最终失败: %v\\n", err)
|
||||||
|
} else {
|
||||||
|
fmt.Printf(" 重试操作成功: %s\\n", data)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println()
|
||||||
|
}
|
||||||
|
|
||||||
|
// demonstratePracticalExamples 演示实际应用场景
|
||||||
|
func demonstratePracticalExamples() {
|
||||||
|
fmt.Println("7. 实际应用场景:")
|
||||||
|
|
||||||
|
// 文件操作错误处理
|
||||||
|
fmt.Printf(" 文件操作错误处理:\\n")
|
||||||
|
|
||||||
|
content, err := safeReadFile("config.json")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf(" 读取配置文件失败: %v\\n", err)
|
||||||
|
} else {
|
||||||
|
fmt.Printf(" 配置内容: %s\\n", content)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 网络请求错误处理
|
||||||
|
fmt.Printf(" 网络请求错误处理:\\n")
|
||||||
|
|
||||||
|
response, err := httpGet("https://api.example.com/users/1")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf(" HTTP请求失败: %v\\n", err)
|
||||||
|
} else {
|
||||||
|
fmt.Printf(" HTTP响应: %s\\n", response)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 数据库操作错误处理
|
||||||
|
fmt.Printf(" 数据库操作错误处理:\\n")
|
||||||
|
|
||||||
|
user := User{ID: 1, Name: "Alice", Email: "alice@example.com"}
|
||||||
|
err = saveUser(user)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf(" 保存用户失败: %v\\n", err)
|
||||||
|
} else {
|
||||||
|
fmt.Printf(" 用户保存成功\\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
// JSON 解析错误处理
|
||||||
|
fmt.Printf(" JSON 解析错误处理:\\n")
|
||||||
|
|
||||||
|
jsonData := `{"name": "Bob", "age": "invalid"}`
|
||||||
|
user, err = parseUser(jsonData)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf(" JSON解析失败: %v\\n", err)
|
||||||
|
} else {
|
||||||
|
fmt.Printf(" 解析用户: %+v\\n", user)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 业务逻辑错误处理
|
||||||
|
fmt.Printf(" 业务逻辑错误处理:\\n")
|
||||||
|
|
||||||
|
account := BankAccount{Balance: 100.0}
|
||||||
|
err = account.Withdraw(150.0)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf(" 取款失败: %v\\n", err)
|
||||||
|
} else {
|
||||||
|
fmt.Printf(" 取款成功,余额: %.2f\\n", account.Balance)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 并发错误处理
|
||||||
|
fmt.Printf(" 并发错误处理:\\n")
|
||||||
|
|
||||||
|
results, errors := processConcurrently([]string{"task1", "task2", "error_task", "task4"})
|
||||||
|
fmt.Printf(" 并发处理结果: %v\\n", results)
|
||||||
|
if len(errors) > 0 {
|
||||||
|
fmt.Printf(" 并发处理错误:\\n")
|
||||||
|
for i, err := range errors {
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf(" 任务%d错误: %v\\n", i, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========== 类型定义和辅助函数 ==========
|
||||||
|
|
||||||
|
// 预定义错误
|
||||||
|
var (
|
||||||
|
ErrInvalidInput = errors.New("输入无效")
|
||||||
|
ErrNotFound = errors.New("未找到")
|
||||||
|
ErrPermissionDenied = errors.New("权限被拒绝")
|
||||||
|
ErrTimeout = errors.New("操作超时")
|
||||||
|
ErrUserNotFound = errors.New("用户未找到")
|
||||||
|
)
|
||||||
|
|
||||||
|
// CustomError 自定义错误类型
|
||||||
|
type CustomError struct {
|
||||||
|
Code int
|
||||||
|
Message string
|
||||||
|
Details string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *CustomError) Error() string {
|
||||||
|
return fmt.Sprintf("错误 %d: %s (%s)", e.Code, e.Message, e.Details)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NetworkError 网络错误类型
|
||||||
|
type NetworkError struct {
|
||||||
|
Op string
|
||||||
|
URL string
|
||||||
|
Temporary bool
|
||||||
|
Err error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *NetworkError) Error() string {
|
||||||
|
return fmt.Sprintf("网络错误 %s %s: %v", e.Op, e.URL, e.Err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// User 用户结构
|
||||||
|
type User struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Email string `json:"email"`
|
||||||
|
Age int `json:"age"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// BankAccount 银行账户
|
||||||
|
type BankAccount struct {
|
||||||
|
Balance float64
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ba *BankAccount) Withdraw(amount float64) error {
|
||||||
|
if amount <= 0 {
|
||||||
|
return fmt.Errorf("取款金额必须大于0,实际: %.2f", amount)
|
||||||
|
}
|
||||||
|
if amount > ba.Balance {
|
||||||
|
return fmt.Errorf("余额不足: 需要 %.2f,可用 %.2f", amount, ba.Balance)
|
||||||
|
}
|
||||||
|
ba.Balance -= amount
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validator 验证器
|
||||||
|
type Validator struct {
|
||||||
|
errors []error
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewValidator() *Validator {
|
||||||
|
return &Validator{errors: make([]error, 0)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *Validator) ValidateRequired(field, value string) {
|
||||||
|
if value == "" {
|
||||||
|
v.errors = append(v.errors, fmt.Errorf("字段 %s 是必需的", field))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *Validator) ValidateEmail(field, value string) {
|
||||||
|
if value != "" && !strings.Contains(value, "@") {
|
||||||
|
v.errors = append(v.errors, fmt.Errorf("字段 %s 不是有效的邮箱地址", field))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *Validator) ValidateRange(field string, value, min, max int) {
|
||||||
|
if value < min || value > max {
|
||||||
|
v.errors = append(v.errors, fmt.Errorf("字段 %s 必须在 %d 到 %d 之间,实际: %d", field, min, max, value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *Validator) HasErrors() bool {
|
||||||
|
return len(v.errors) > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *Validator) Errors() []error {
|
||||||
|
return v.errors
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========== 辅助函数 ==========
|
||||||
|
|
||||||
|
// divide 除法运算
|
||||||
|
func divide(a, b float64) (float64, error) {
|
||||||
|
if b == 0 {
|
||||||
|
return 0, errors.New("除数不能为零")
|
||||||
|
}
|
||||||
|
return a / b, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// validateAge 验证年龄
|
||||||
|
func validateAge(age int) error {
|
||||||
|
if age < 0 {
|
||||||
|
return fmt.Errorf("年龄不能为负数: %d", age)
|
||||||
|
}
|
||||||
|
if age > 150 {
|
||||||
|
return fmt.Errorf("年龄不能超过150: %d", age)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// readFile 读取文件
|
||||||
|
func readFile(filename string) (string, error) {
|
||||||
|
// 模拟文件读取
|
||||||
|
if filename == "example.txt" {
|
||||||
|
return "", fmt.Errorf("文件 %s 不存在", filename)
|
||||||
|
}
|
||||||
|
return "文件内容", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// performMultipleOperations 执行多个操作
|
||||||
|
func performMultipleOperations() error {
|
||||||
|
// 模拟多个操作
|
||||||
|
operations := []func() error{
|
||||||
|
func() error { return nil }, // 成功
|
||||||
|
func() error { return nil }, // 成功
|
||||||
|
func() error { return errors.New("第三个操作失败") }, // 失败
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, op := range operations {
|
||||||
|
if err := op(); err != nil {
|
||||||
|
return fmt.Errorf("操作 %d 失败: %w", i+1, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// handleSpecificError 处理特定错误
|
||||||
|
func handleSpecificError(err error) {
|
||||||
|
if err == nil {
|
||||||
|
fmt.Printf("成功\\n")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case errors.Is(err, ErrNotFound):
|
||||||
|
fmt.Printf("资源未找到\\n")
|
||||||
|
case errors.Is(err, ErrPermissionDenied):
|
||||||
|
fmt.Printf("权限不足\\n")
|
||||||
|
default:
|
||||||
|
fmt.Printf("未知错误: %v\\n", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// fetchDataWithRetry 带重试的数据获取
|
||||||
|
func fetchDataWithRetry(url string, maxRetries int) (string, error) {
|
||||||
|
var lastErr error
|
||||||
|
|
||||||
|
for i := 0; i < maxRetries; i++ {
|
||||||
|
// 模拟网络请求
|
||||||
|
if rand.Float32() < 0.3 { // 30% 成功率
|
||||||
|
return fmt.Sprintf("数据来自 %s", url), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
lastErr = fmt.Errorf("请求 %s 失败 (尝试 %d/%d)", url, i+1, maxRetries)
|
||||||
|
time.Sleep(100 * time.Millisecond) // 重试延迟
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", fmt.Errorf("重试 %d 次后仍然失败: %w", maxRetries, lastErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// processBatch 批量处理
|
||||||
|
func processBatch(items []string) ([]string, []error) {
|
||||||
|
var results []string
|
||||||
|
var errors []error
|
||||||
|
|
||||||
|
for _, item := range items {
|
||||||
|
if item == "invalid" {
|
||||||
|
errors = append(errors, fmt.Errorf("无效项目: %s", item))
|
||||||
|
} else {
|
||||||
|
results = append(results, fmt.Sprintf("处理后的%s", item))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return results, errors
|
||||||
|
}
|
||||||
|
|
||||||
|
// calculateTotal 计算总和
|
||||||
|
func calculateTotal(values []float64) (float64, error) {
|
||||||
|
total := 0.0
|
||||||
|
for i, value := range values {
|
||||||
|
if value < 0 {
|
||||||
|
return 0, fmt.Errorf("索引 %d 的值不能为负数: %.2f", i, value)
|
||||||
|
}
|
||||||
|
total += value
|
||||||
|
}
|
||||||
|
return total, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// getUserProfile 获取用户资料
|
||||||
|
func getUserProfile(username string) (*User, error) {
|
||||||
|
// 模拟数据库查询
|
||||||
|
if username == "nonexistent_user" {
|
||||||
|
return nil, fmt.Errorf("获取用户资料失败: %w", ErrUserNotFound)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &User{
|
||||||
|
ID: 1,
|
||||||
|
Name: username,
|
||||||
|
Email: username + "@example.com",
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// processOrder 处理订单
|
||||||
|
func processOrder(orderID string) error {
|
||||||
|
// 模拟订单处理链
|
||||||
|
if err := validateOrder(orderID); err != nil {
|
||||||
|
return fmt.Errorf("处理订单 %s 失败: %w", orderID, err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// validateOrder 验证订单
|
||||||
|
func validateOrder(orderID string) error {
|
||||||
|
if orderID == "invalid_order_id" {
|
||||||
|
if err := checkOrderExists(orderID); err != nil {
|
||||||
|
return fmt.Errorf("订单验证失败: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// checkOrderExists 检查订单是否存在
|
||||||
|
func checkOrderExists(orderID string) error {
|
||||||
|
return fmt.Errorf("数据库查询失败: %w", ErrNotFound)
|
||||||
|
}
|
||||||
|
|
||||||
|
// printErrorChain 打印错误链
|
||||||
|
func printErrorChain(err error, indent string) {
|
||||||
|
if err == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("%s- %v\\n", indent, err)
|
||||||
|
|
||||||
|
if unwrapped := errors.Unwrap(err); unwrapped != nil {
|
||||||
|
printErrorChain(unwrapped, indent+" ")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// processItem 处理项目
|
||||||
|
func processItem(item string) (string, error) {
|
||||||
|
switch item {
|
||||||
|
case "invalid":
|
||||||
|
return "", ErrInvalidInput
|
||||||
|
case "error":
|
||||||
|
return "", errors.New("处理错误")
|
||||||
|
default:
|
||||||
|
return fmt.Sprintf("处理后的%s", item), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// findUser 查找用户
|
||||||
|
func findUser(username string) (*User, error) {
|
||||||
|
if username == "nonexistent" {
|
||||||
|
return nil, ErrUserNotFound
|
||||||
|
}
|
||||||
|
return &User{Name: username}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// performNetworkOperation 执行网络操作
|
||||||
|
func performNetworkOperation() error {
|
||||||
|
return &NetworkError{
|
||||||
|
Op: "GET",
|
||||||
|
URL: "https://api.example.com",
|
||||||
|
Temporary: true,
|
||||||
|
Err: errors.New("连接超时"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// callExternalAPI 调用外部API
|
||||||
|
func callExternalAPI() error {
|
||||||
|
return errors.New("外部服务不可用")
|
||||||
|
}
|
||||||
|
|
||||||
|
// performWithFallback 带回退的操作
|
||||||
|
func performWithFallback() string {
|
||||||
|
// 尝试主要操作
|
||||||
|
if err := primaryOperation(); err != nil {
|
||||||
|
// 主要操作失败,使用回退
|
||||||
|
return fallbackOperation()
|
||||||
|
}
|
||||||
|
return "主要操作成功"
|
||||||
|
}
|
||||||
|
|
||||||
|
// primaryOperation 主要操作
|
||||||
|
func primaryOperation() error {
|
||||||
|
return errors.New("主要操作失败")
|
||||||
|
}
|
||||||
|
|
||||||
|
// fallbackOperation 回退操作
|
||||||
|
func fallbackOperation() string {
|
||||||
|
return "回退操作成功"
|
||||||
|
}
|
||||||
|
|
||||||
|
// retryOperation 重试操作
|
||||||
|
func retryOperation(op func() (string, error), maxRetries int) (string, error) {
|
||||||
|
var lastErr error
|
||||||
|
|
||||||
|
for i := 0; i < maxRetries; i++ {
|
||||||
|
result, err := op()
|
||||||
|
if err == nil {
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
lastErr = err
|
||||||
|
fmt.Printf(" 尝试 %d/%d 失败: %v\\n", i+1, maxRetries, err)
|
||||||
|
|
||||||
|
if i < maxRetries-1 {
|
||||||
|
time.Sleep(100 * time.Millisecond)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", fmt.Errorf("重试 %d 次后失败: %w", maxRetries, lastErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// safeReadFile 安全读取文件
|
||||||
|
func safeReadFile(filename string) (string, error) {
|
||||||
|
// 模拟文件读取
|
||||||
|
switch filename {
|
||||||
|
case "config.json":
|
||||||
|
return "", fmt.Errorf("读取文件 %s 失败: %w", filename, os.ErrNotExist)
|
||||||
|
default:
|
||||||
|
return "文件内容", nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// httpGet HTTP GET 请求
|
||||||
|
func httpGet(url string) (string, error) {
|
||||||
|
// 模拟HTTP请求
|
||||||
|
if strings.Contains(url, "example.com") {
|
||||||
|
return "", &NetworkError{
|
||||||
|
Op: "GET",
|
||||||
|
URL: url,
|
||||||
|
Temporary: false,
|
||||||
|
Err: errors.New("域名解析失败"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "HTTP响应内容", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// saveUser 保存用户
|
||||||
|
func saveUser(user User) error {
|
||||||
|
// 模拟数据库保存
|
||||||
|
if user.Email == "" {
|
||||||
|
return fmt.Errorf("保存用户失败: %w", ErrInvalidInput)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseUser 解析用户JSON
|
||||||
|
func parseUser(jsonData string) (User, error) {
|
||||||
|
var user User
|
||||||
|
if err := json.Unmarshal([]byte(jsonData), &user); err != nil {
|
||||||
|
return User{}, fmt.Errorf("解析用户JSON失败: %w", err)
|
||||||
|
}
|
||||||
|
return user, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// processConcurrently 并发处理
|
||||||
|
func processConcurrently(tasks []string) ([]string, []error) {
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
results := make([]string, len(tasks))
|
||||||
|
errors := make([]error, len(tasks))
|
||||||
|
|
||||||
|
for i, task := range tasks {
|
||||||
|
wg.Add(1)
|
||||||
|
go func(index int, taskName string) {
|
||||||
|
defer wg.Done()
|
||||||
|
|
||||||
|
// 模拟任务处理
|
||||||
|
if strings.Contains(taskName, "error") {
|
||||||
|
errors[index] = fmt.Errorf("任务 %s 处理失败", taskName)
|
||||||
|
} else {
|
||||||
|
results[index] = fmt.Sprintf("处理后的%s", taskName)
|
||||||
|
}
|
||||||
|
}(i, task)
|
||||||
|
}
|
||||||
|
|
||||||
|
wg.Wait()
|
||||||
|
return results, errors
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
运行这个程序:
|
||||||
|
go run 01-basic-errors.go
|
||||||
|
|
||||||
|
学习要点:
|
||||||
|
1. Go 语言使用显式错误处理,错误是值
|
||||||
|
2. error 接口只有一个 Error() string 方法
|
||||||
|
3. 使用多返回值模式处理错误
|
||||||
|
4. 错误应该被检查和处理,不应该被忽略
|
||||||
|
5. 错误信息应该清晰、有用、包含上下文
|
||||||
|
|
||||||
|
错误处理的特点:
|
||||||
|
1. 显式性:错误必须被显式检查
|
||||||
|
2. 简单性:error 接口设计简单
|
||||||
|
3. 组合性:错误可以被包装和组合
|
||||||
|
4. 传播性:错误可以在调用栈中传播
|
||||||
|
5. 灵活性:支持多种错误处理模式
|
||||||
|
|
||||||
|
错误创建方式:
|
||||||
|
1. errors.New():创建简单错误
|
||||||
|
2. fmt.Errorf():创建格式化错误
|
||||||
|
3. 自定义错误类型:实现 error 接口
|
||||||
|
4. 预定义错误:定义常用错误变量
|
||||||
|
5. 错误包装:使用 %w 动词包装错误
|
||||||
|
|
||||||
|
错误检查模式:
|
||||||
|
1. 立即检查:if err != nil { ... }
|
||||||
|
2. 错误传播:return err 或 return fmt.Errorf("...: %w", err)
|
||||||
|
3. 错误类型检查:errors.Is() 和 errors.As()
|
||||||
|
4. 错误恢复:提供默认值或回退方案
|
||||||
|
5. 错误聚合:收集多个错误
|
||||||
|
|
||||||
|
错误处理模式:
|
||||||
|
1. 哨兵错误:预定义的错误值
|
||||||
|
2. 错误类型:自定义错误结构体
|
||||||
|
3. 不透明错误:不检查具体错误类型
|
||||||
|
4. 错误包装:添加上下文信息
|
||||||
|
5. 错误聚合:收集和批量处理错误
|
||||||
|
|
||||||
|
最佳实践:
|
||||||
|
1. 错误信息应该小写开头,不以标点结尾
|
||||||
|
2. 错误信息应该包含足够的上下文
|
||||||
|
3. 使用错误包装传播上下文
|
||||||
|
4. 不要忽略错误,至少要记录日志
|
||||||
|
5. 在适当的层级处理错误
|
||||||
|
|
||||||
|
常见陷阱:
|
||||||
|
1. 忽略错误返回值
|
||||||
|
2. 错误信息不够清晰
|
||||||
|
3. 过度包装错误
|
||||||
|
4. 不适当的错误类型检查
|
||||||
|
5. 在错误处理中引入新的错误
|
||||||
|
|
||||||
|
性能考虑:
|
||||||
|
1. 错误创建有一定开销
|
||||||
|
2. 错误包装会增加内存使用
|
||||||
|
3. 错误检查是必要的开销
|
||||||
|
4. 避免在热点路径创建复杂错误
|
||||||
|
5. 合理使用预定义错误
|
||||||
|
|
||||||
|
注意事项:
|
||||||
|
1. nil 错误表示没有错误
|
||||||
|
2. 错误比较使用 == 或 errors.Is()
|
||||||
|
3. 错误类型断言使用 errors.As()
|
||||||
|
4. 错误包装使用 fmt.Errorf() 和 %w
|
||||||
|
5. 错误解包使用 errors.Unwrap()
|
||||||
|
*/
|
1071
golang-learning/07-error-handling/02-custom-errors.go
Normal file
1071
golang-learning/07-error-handling/02-custom-errors.go
Normal file
File diff suppressed because it is too large
Load Diff
941
golang-learning/07-error-handling/03-panic-recover.go
Normal file
941
golang-learning/07-error-handling/03-panic-recover.go
Normal file
@@ -0,0 +1,941 @@
|
|||||||
|
/*
|
||||||
|
03-panic-recover.go - Go 语言 Panic 和 Recover 详解
|
||||||
|
|
||||||
|
学习目标:
|
||||||
|
1. 理解 panic 的概念和使用场景
|
||||||
|
2. 掌握 recover 的恢复机制
|
||||||
|
3. 学会 defer 与 panic/recover 的配合
|
||||||
|
4. 了解 panic 的传播机制
|
||||||
|
5. 掌握 panic/recover 的最佳实践
|
||||||
|
|
||||||
|
知识点:
|
||||||
|
- panic 的触发条件和行为
|
||||||
|
- recover 的恢复机制
|
||||||
|
- defer 语句的执行顺序
|
||||||
|
- panic 的传播和栈展开
|
||||||
|
- panic/recover 的实际应用
|
||||||
|
- 错误处理 vs panic 的选择
|
||||||
|
*/
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"runtime"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
fmt.Println("=== Go 语言 Panic 和 Recover 详解 ===\\n")
|
||||||
|
|
||||||
|
// 演示 panic 的基本概念
|
||||||
|
demonstratePanicBasics()
|
||||||
|
|
||||||
|
// 演示 recover 的使用
|
||||||
|
demonstrateRecover()
|
||||||
|
|
||||||
|
// 演示 defer 与 panic/recover 的配合
|
||||||
|
demonstrateDeferWithPanicRecover()
|
||||||
|
|
||||||
|
// 演示 panic 的传播
|
||||||
|
demonstratePanicPropagation()
|
||||||
|
|
||||||
|
// 演示 panic/recover 的实际应用
|
||||||
|
demonstratePracticalUsage()
|
||||||
|
|
||||||
|
// 演示最佳实践
|
||||||
|
demonstrateBestPractices()
|
||||||
|
|
||||||
|
// 演示错误处理 vs panic 的选择
|
||||||
|
demonstrateErrorVsPanic()
|
||||||
|
}
|
||||||
|
|
||||||
|
// demonstratePanicBasics 演示 panic 的基本概念
|
||||||
|
func demonstratePanicBasics() {
|
||||||
|
fmt.Println("1. Panic 的基本概念:")
|
||||||
|
|
||||||
|
// panic 的基本特性
|
||||||
|
fmt.Printf(" Panic 的基本特性:\\n")
|
||||||
|
fmt.Printf(" - panic 是运行时错误,会导致程序崩溃\\n")
|
||||||
|
fmt.Printf(" - panic 会停止当前函数的执行\\n")
|
||||||
|
fmt.Printf(" - panic 会执行当前函数的 defer 语句\\n")
|
||||||
|
fmt.Printf(" - panic 会向上传播到调用栈\\n")
|
||||||
|
fmt.Printf(" - panic 可以被 recover 捕获\\n")
|
||||||
|
|
||||||
|
// 手动触发 panic
|
||||||
|
fmt.Printf(" 手动触发 panic:\\n")
|
||||||
|
|
||||||
|
func() {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
fmt.Printf(" 捕获到 panic: %v\\n", r)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
fmt.Printf(" 准备触发 panic\\n")
|
||||||
|
panic("这是一个手动触发的 panic")
|
||||||
|
fmt.Printf(" 这行代码不会执行\\n")
|
||||||
|
}()
|
||||||
|
|
||||||
|
// 常见的 panic 场景
|
||||||
|
fmt.Printf(" 常见的 panic 场景:\\n")
|
||||||
|
|
||||||
|
// 1. 数组越界
|
||||||
|
func() {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
fmt.Printf(" 数组越界 panic: %v\\n", r)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
arr := [3]int{1, 2, 3}
|
||||||
|
fmt.Printf(" 访问数组索引 5: %d\\n", arr[5]) // 会 panic
|
||||||
|
}()
|
||||||
|
|
||||||
|
// 2. 空指针解引用
|
||||||
|
func() {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
fmt.Printf(" 空指针解引用 panic: %v\\n", r)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
var ptr *int
|
||||||
|
fmt.Printf(" 解引用空指针: %d\\n", *ptr) // 会 panic
|
||||||
|
}()
|
||||||
|
|
||||||
|
// 3. 类型断言失败
|
||||||
|
func() {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
fmt.Printf(" 类型断言失败 panic: %v\\n", r)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
var x interface{} = "hello"
|
||||||
|
num := x.(int) // 会 panic
|
||||||
|
fmt.Printf(" 类型断言结果: %d\\n", num)
|
||||||
|
}()
|
||||||
|
|
||||||
|
// 4. 向已关闭的 channel 发送数据
|
||||||
|
func() {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
fmt.Printf(" 向已关闭 channel 发送数据 panic: %v\\n", r)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
ch := make(chan int)
|
||||||
|
close(ch)
|
||||||
|
ch <- 1 // 会 panic
|
||||||
|
}()
|
||||||
|
|
||||||
|
fmt.Println()
|
||||||
|
}
|
||||||
|
|
||||||
|
// demonstrateRecover 演示 recover 的使用
|
||||||
|
func demonstrateRecover() {
|
||||||
|
fmt.Println("2. Recover 的使用:")
|
||||||
|
|
||||||
|
// recover 的基本概念
|
||||||
|
fmt.Printf(" Recover 的基本概念:\\n")
|
||||||
|
fmt.Printf(" - recover 只能在 defer 函数中使用\\n")
|
||||||
|
fmt.Printf(" - recover 可以捕获当前 goroutine 的 panic\\n")
|
||||||
|
fmt.Printf(" - recover 返回 panic 的值\\n")
|
||||||
|
fmt.Printf(" - recover 后程序可以继续执行\\n")
|
||||||
|
|
||||||
|
// 基本 recover 示例
|
||||||
|
fmt.Printf(" 基本 recover 示例:\\n")
|
||||||
|
|
||||||
|
result := safeFunction(func() int {
|
||||||
|
panic("计算过程中发生错误")
|
||||||
|
return 42
|
||||||
|
})
|
||||||
|
fmt.Printf(" 安全函数返回: %d\\n", result)
|
||||||
|
|
||||||
|
// recover 的返回值
|
||||||
|
fmt.Printf(" recover 的返回值:\\n")
|
||||||
|
|
||||||
|
func() {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
fmt.Printf(" panic 值: %v\\n", r)
|
||||||
|
fmt.Printf(" panic 类型: %T\\n", r)
|
||||||
|
|
||||||
|
// 根据 panic 类型进行不同处理
|
||||||
|
switch v := r.(type) {
|
||||||
|
case string:
|
||||||
|
fmt.Printf(" 字符串 panic: %s\\n", v)
|
||||||
|
case int:
|
||||||
|
fmt.Printf(" 整数 panic: %d\\n", v)
|
||||||
|
case error:
|
||||||
|
fmt.Printf(" 错误 panic: %v\\n", v)
|
||||||
|
default:
|
||||||
|
fmt.Printf(" 其他类型 panic: %v\\n", v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// 触发不同类型的 panic
|
||||||
|
testPanics := []interface{}{
|
||||||
|
"字符串错误",
|
||||||
|
42,
|
||||||
|
fmt.Errorf("错误对象"),
|
||||||
|
struct{ Message string }{"结构体错误"},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, panicValue := range testPanics {
|
||||||
|
func() {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
fmt.Printf(" 测试 %d - panic: %v (%T)\\n", i+1, r, r)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
panic(panicValue)
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// recover 的条件
|
||||||
|
fmt.Printf(" recover 的条件:\\n")
|
||||||
|
|
||||||
|
// 正确的 recover 使用
|
||||||
|
func() {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
fmt.Printf(" 正确捕获 panic: %v\\n", r)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
panic("正确的 recover 示例")
|
||||||
|
}()
|
||||||
|
|
||||||
|
// 错误的 recover 使用
|
||||||
|
func() {
|
||||||
|
defer func() {
|
||||||
|
// 这个 recover 不会捕获到 panic,因为不是直接在 defer 中调用
|
||||||
|
go func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
fmt.Printf(" 错误的 recover: %v\\n", r)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}()
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
fmt.Printf(" 正确捕获嵌套 panic: %v\\n", r)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
panic("嵌套 panic 示例")
|
||||||
|
}()
|
||||||
|
|
||||||
|
fmt.Println()
|
||||||
|
}
|
||||||
|
|
||||||
|
// demonstrateDeferWithPanicRecover 演示 defer 与 panic/recover 的配合
|
||||||
|
func demonstrateDeferWithPanicRecover() {
|
||||||
|
fmt.Println("3. Defer 与 Panic/Recover 的配合:")
|
||||||
|
|
||||||
|
// defer 的执行顺序
|
||||||
|
fmt.Printf(" defer 的执行顺序:\\n")
|
||||||
|
|
||||||
|
func() {
|
||||||
|
defer fmt.Printf(" defer 1\\n")
|
||||||
|
defer fmt.Printf(" defer 2\\n")
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
fmt.Printf(" recover: %v\\n", r)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
defer fmt.Printf(" defer 3\\n")
|
||||||
|
|
||||||
|
fmt.Printf(" 正常执行\\n")
|
||||||
|
panic("测试 defer 顺序")
|
||||||
|
fmt.Printf(" 这行不会执行\\n")
|
||||||
|
}()
|
||||||
|
|
||||||
|
// 多层 defer 和 recover
|
||||||
|
fmt.Printf(" 多层 defer 和 recover:\\n")
|
||||||
|
|
||||||
|
func() {
|
||||||
|
defer func() {
|
||||||
|
fmt.Printf(" 外层 defer 开始\\n")
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
fmt.Printf(" 外层 recover: %v\\n", r)
|
||||||
|
}
|
||||||
|
fmt.Printf(" 外层 defer 结束\\n")
|
||||||
|
}()
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
fmt.Printf(" 内层 defer 开始\\n")
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
fmt.Printf(" 内层 recover: %v\\n", r)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
fmt.Printf(" 内层 defer 结束\\n")
|
||||||
|
}()
|
||||||
|
|
||||||
|
panic("多层 defer 测试")
|
||||||
|
}()
|
||||||
|
|
||||||
|
// defer 中的资源清理
|
||||||
|
fmt.Printf(" defer 中的资源清理:\\n")
|
||||||
|
|
||||||
|
processWithCleanup := func() {
|
||||||
|
// 模拟资源分配
|
||||||
|
resource := "重要资源"
|
||||||
|
fmt.Printf(" 分配资源: %s\\n", resource)
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
// 资源清理
|
||||||
|
fmt.Printf(" 清理资源: %s\\n", resource)
|
||||||
|
|
||||||
|
// 捕获 panic
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
fmt.Printf(" 处理过程中发生 panic: %v\\n", r)
|
||||||
|
fmt.Printf(" 资源已安全清理\\n")
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// 模拟可能 panic 的操作
|
||||||
|
if time.Now().UnixNano()%2 == 0 {
|
||||||
|
panic("随机 panic")
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf(" 正常处理完成\\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 执行多次以演示不同情况
|
||||||
|
for i := 0; i < 3; i++ {
|
||||||
|
fmt.Printf(" 执行 %d:\\n", i+1)
|
||||||
|
processWithCleanup()
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println()
|
||||||
|
}
|
||||||
|
|
||||||
|
// demonstratePanicPropagation 演示 panic 的传播
|
||||||
|
func demonstratePanicPropagation() {
|
||||||
|
fmt.Println("4. Panic 的传播:")
|
||||||
|
|
||||||
|
// panic 的栈展开
|
||||||
|
fmt.Printf(" panic 的栈展开:\\n")
|
||||||
|
|
||||||
|
func() {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
fmt.Printf(" 最终捕获 panic: %v\\n", r)
|
||||||
|
|
||||||
|
// 打印调用栈
|
||||||
|
buf := make([]byte, 1024)
|
||||||
|
n := runtime.Stack(buf, false)
|
||||||
|
fmt.Printf(" 调用栈:\\n%s\\n", buf[:n])
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
level1()
|
||||||
|
}()
|
||||||
|
|
||||||
|
// panic 的中途拦截
|
||||||
|
fmt.Printf(" panic 的中途拦截:\\n")
|
||||||
|
|
||||||
|
func() {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
fmt.Printf(" 最外层捕获: %v\\n", r)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
interceptedLevel1()
|
||||||
|
}()
|
||||||
|
|
||||||
|
// panic 的重新抛出
|
||||||
|
fmt.Printf(" panic 的重新抛出:\\n")
|
||||||
|
|
||||||
|
func() {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
fmt.Printf(" 最终处理 panic: %v\\n", r)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
rethrowLevel1()
|
||||||
|
}()
|
||||||
|
|
||||||
|
fmt.Println()
|
||||||
|
}
|
||||||
|
|
||||||
|
// demonstratePracticalUsage 演示 panic/recover 的实际应用
|
||||||
|
func demonstratePracticalUsage() {
|
||||||
|
fmt.Println("5. Panic/Recover 的实际应用:")
|
||||||
|
|
||||||
|
// Web 服务器中的 panic 恢复
|
||||||
|
fmt.Printf(" Web 服务器中的 panic 恢复:\\n")
|
||||||
|
|
||||||
|
requests := []string{
|
||||||
|
"/api/users",
|
||||||
|
"/api/panic",
|
||||||
|
"/api/orders",
|
||||||
|
"/api/error",
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, path := range requests {
|
||||||
|
handleHTTPRequest(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 工作池中的 panic 处理
|
||||||
|
fmt.Printf(" 工作池中的 panic 处理:\\n")
|
||||||
|
|
||||||
|
jobs := []Job{
|
||||||
|
{ID: 1, Data: "正常任务"},
|
||||||
|
{ID: 2, Data: "panic任务"},
|
||||||
|
{ID: 3, Data: "正常任务"},
|
||||||
|
{ID: 4, Data: "error任务"},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, job := range jobs {
|
||||||
|
processJobSafely(job)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 数据解析中的 panic 处理
|
||||||
|
fmt.Printf(" 数据解析中的 panic 处理:\\n")
|
||||||
|
|
||||||
|
data := []string{
|
||||||
|
"123",
|
||||||
|
"abc",
|
||||||
|
"456",
|
||||||
|
"",
|
||||||
|
"789",
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, item := range data {
|
||||||
|
result := parseIntSafely(item)
|
||||||
|
fmt.Printf(" 解析 '%s': %d\\n", item, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 递归函数中的 panic 处理
|
||||||
|
fmt.Printf(" 递归函数中的 panic 处理:\\n")
|
||||||
|
|
||||||
|
testValues := []int{5, -1, 10, 0}
|
||||||
|
for _, n := range testValues {
|
||||||
|
result := safeFactorial(n)
|
||||||
|
fmt.Printf(" %d! = %d\\n", n, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println()
|
||||||
|
}
|
||||||
|
|
||||||
|
// demonstrateBestPractices 演示最佳实践
|
||||||
|
func demonstrateBestPractices() {
|
||||||
|
fmt.Println("6. 最佳实践:")
|
||||||
|
|
||||||
|
// 最佳实践原则
|
||||||
|
fmt.Printf(" 最佳实践原则:\\n")
|
||||||
|
fmt.Printf(" 1. 优先使用错误返回值而不是 panic\\n")
|
||||||
|
fmt.Printf(" 2. 只在真正异常的情况下使用 panic\\n")
|
||||||
|
fmt.Printf(" 3. 在库代码中避免 panic,转换为错误\\n")
|
||||||
|
fmt.Printf(" 4. 在应用边界使用 recover 保护\\n")
|
||||||
|
fmt.Printf(" 5. 记录 panic 信息用于调试\\n")
|
||||||
|
|
||||||
|
// 库函数的 panic 处理
|
||||||
|
fmt.Printf(" 库函数的 panic 处理:\\n")
|
||||||
|
|
||||||
|
// 不好的做法:库函数直接 panic
|
||||||
|
fmt.Printf(" 不好的做法示例:\\n")
|
||||||
|
result, err := callLibraryFunction("invalid")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf(" 库函数错误: %v\\n", err)
|
||||||
|
} else {
|
||||||
|
fmt.Printf(" 库函数结果: %s\\n", result)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 好的做法:将 panic 转换为错误
|
||||||
|
fmt.Printf(" 好的做法示例:\\n")
|
||||||
|
result, err = safeLibraryFunction("invalid")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf(" 安全库函数错误: %v\\n", err)
|
||||||
|
} else {
|
||||||
|
fmt.Printf(" 安全库函数结果: %s\\n", result)
|
||||||
|
}
|
||||||
|
|
||||||
|
// panic 信息的记录
|
||||||
|
fmt.Printf(" panic 信息的记录:\\n")
|
||||||
|
|
||||||
|
func() {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
logPanic(r)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
panic("需要记录的 panic")
|
||||||
|
}()
|
||||||
|
|
||||||
|
// 优雅的服务关闭
|
||||||
|
fmt.Printf(" 优雅的服务关闭:\\n")
|
||||||
|
|
||||||
|
server := &Server{name: "测试服务器"}
|
||||||
|
server.Start()
|
||||||
|
|
||||||
|
// 模拟服务运行中的 panic
|
||||||
|
func() {
|
||||||
|
defer server.handlePanic()
|
||||||
|
panic("服务运行时错误")
|
||||||
|
}()
|
||||||
|
|
||||||
|
server.Stop()
|
||||||
|
|
||||||
|
fmt.Println()
|
||||||
|
}
|
||||||
|
|
||||||
|
// demonstrateErrorVsPanic 演示错误处理 vs panic 的选择
|
||||||
|
func demonstrateErrorVsPanic() {
|
||||||
|
fmt.Println("7. 错误处理 vs Panic 的选择:")
|
||||||
|
|
||||||
|
// 使用错误的场景
|
||||||
|
fmt.Printf(" 使用错误的场景:\\n")
|
||||||
|
fmt.Printf(" - 预期可能发生的错误\\n")
|
||||||
|
fmt.Printf(" - 用户输入错误\\n")
|
||||||
|
fmt.Printf(" - 网络或I/O错误\\n")
|
||||||
|
fmt.Printf(" - 业务逻辑错误\\n")
|
||||||
|
|
||||||
|
// 使用 panic 的场景
|
||||||
|
fmt.Printf(" 使用 panic 的场景:\\n")
|
||||||
|
fmt.Printf(" - 程序逻辑错误\\n")
|
||||||
|
fmt.Printf(" - 不可恢复的错误\\n")
|
||||||
|
fmt.Printf(" - 初始化失败\\n")
|
||||||
|
fmt.Printf(" - 断言失败\\n")
|
||||||
|
|
||||||
|
// 对比示例
|
||||||
|
fmt.Printf(" 对比示例:\\n")
|
||||||
|
|
||||||
|
// 错误处理示例
|
||||||
|
fmt.Printf(" 错误处理示例:\\n")
|
||||||
|
result, err := divide(10, 0)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf(" 除法错误: %v\\n", err)
|
||||||
|
} else {
|
||||||
|
fmt.Printf(" 除法结果: %.2f\\n", result)
|
||||||
|
}
|
||||||
|
|
||||||
|
// panic 示例
|
||||||
|
fmt.Printf(" panic 示例:\\n")
|
||||||
|
func() {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
fmt.Printf(" 捕获 panic: %v\\n", r)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
assertPositive(-5)
|
||||||
|
}()
|
||||||
|
|
||||||
|
// 混合使用示例
|
||||||
|
fmt.Printf(" 混合使用示例:\\n")
|
||||||
|
|
||||||
|
calculator := &Calculator{}
|
||||||
|
|
||||||
|
// 正常计算
|
||||||
|
result, err = calculator.Calculate("10 + 5")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf(" 计算错误: %v\\n", err)
|
||||||
|
} else {
|
||||||
|
fmt.Printf(" 计算结果: %.2f\\n", result)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 无效表达式
|
||||||
|
result, err = calculator.Calculate("invalid")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf(" 计算错误: %v\\n", err)
|
||||||
|
} else {
|
||||||
|
fmt.Printf(" 计算结果: %.2f\\n", result)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 内部 panic 被转换为错误
|
||||||
|
result, err = calculator.Calculate("panic")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf(" 计算错误: %v\\n", err)
|
||||||
|
} else {
|
||||||
|
fmt.Printf(" 计算结果: %.2f\\n", result)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========== 辅助函数和类型定义 ==========
|
||||||
|
|
||||||
|
// safeFunction 安全执行函数
|
||||||
|
func safeFunction(fn func() int) int {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
fmt.Printf(" 函数执行中发生 panic,已恢复: %v\\n", r)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return fn()
|
||||||
|
}
|
||||||
|
|
||||||
|
// level1 第一层函数
|
||||||
|
func level1() {
|
||||||
|
defer func() {
|
||||||
|
fmt.Printf(" level1 defer 执行\\n")
|
||||||
|
}()
|
||||||
|
|
||||||
|
fmt.Printf(" 进入 level1\\n")
|
||||||
|
level2()
|
||||||
|
fmt.Printf(" level1 正常结束\\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
// level2 第二层函数
|
||||||
|
func level2() {
|
||||||
|
defer func() {
|
||||||
|
fmt.Printf(" level2 defer 执行\\n")
|
||||||
|
}()
|
||||||
|
|
||||||
|
fmt.Printf(" 进入 level2\\n")
|
||||||
|
level3()
|
||||||
|
fmt.Printf(" level2 正常结束\\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
// level3 第三层函数
|
||||||
|
func level3() {
|
||||||
|
defer func() {
|
||||||
|
fmt.Printf(" level3 defer 执行\\n")
|
||||||
|
}()
|
||||||
|
|
||||||
|
fmt.Printf(" 进入 level3\\n")
|
||||||
|
panic("level3 中的 panic")
|
||||||
|
fmt.Printf(" level3 正常结束\\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
// interceptedLevel1 中途拦截的第一层
|
||||||
|
func interceptedLevel1() {
|
||||||
|
defer func() {
|
||||||
|
fmt.Printf(" interceptedLevel1 defer\\n")
|
||||||
|
}()
|
||||||
|
|
||||||
|
fmt.Printf(" 进入 interceptedLevel1\\n")
|
||||||
|
interceptedLevel2()
|
||||||
|
fmt.Printf(" interceptedLevel1 正常结束\\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
// interceptedLevel2 中途拦截的第二层
|
||||||
|
func interceptedLevel2() {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
fmt.Printf(" interceptedLevel2 拦截 panic: %v\\n", r)
|
||||||
|
// 不重新抛出,panic 在这里被处理
|
||||||
|
}
|
||||||
|
fmt.Printf(" interceptedLevel2 defer\\n")
|
||||||
|
}()
|
||||||
|
|
||||||
|
fmt.Printf(" 进入 interceptedLevel2\\n")
|
||||||
|
interceptedLevel3()
|
||||||
|
fmt.Printf(" interceptedLevel2 正常结束\\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
// interceptedLevel3 中途拦截的第三层
|
||||||
|
func interceptedLevel3() {
|
||||||
|
defer func() {
|
||||||
|
fmt.Printf(" interceptedLevel3 defer\\n")
|
||||||
|
}()
|
||||||
|
|
||||||
|
fmt.Printf(" 进入 interceptedLevel3\\n")
|
||||||
|
panic("interceptedLevel3 中的 panic")
|
||||||
|
fmt.Printf(" interceptedLevel3 正常结束\\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
// rethrowLevel1 重新抛出的第一层
|
||||||
|
func rethrowLevel1() {
|
||||||
|
defer func() {
|
||||||
|
fmt.Printf(" rethrowLevel1 defer\\n")
|
||||||
|
}()
|
||||||
|
|
||||||
|
fmt.Printf(" 进入 rethrowLevel1\\n")
|
||||||
|
rethrowLevel2()
|
||||||
|
fmt.Printf(" rethrowLevel1 正常结束\\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
// rethrowLevel2 重新抛出的第二层
|
||||||
|
func rethrowLevel2() {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
fmt.Printf(" rethrowLevel2 捕获并重新抛出 panic: %v\\n", r)
|
||||||
|
panic(fmt.Sprintf("重新抛出: %v", r)) // 重新抛出
|
||||||
|
}
|
||||||
|
fmt.Printf(" rethrowLevel2 defer\\n")
|
||||||
|
}()
|
||||||
|
|
||||||
|
fmt.Printf(" 进入 rethrowLevel2\\n")
|
||||||
|
rethrowLevel3()
|
||||||
|
fmt.Printf(" rethrowLevel2 正常结束\\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
// rethrowLevel3 重新抛出的第三层
|
||||||
|
func rethrowLevel3() {
|
||||||
|
defer func() {
|
||||||
|
fmt.Printf(" rethrowLevel3 defer\\n")
|
||||||
|
}()
|
||||||
|
|
||||||
|
fmt.Printf(" 进入 rethrowLevel3\\n")
|
||||||
|
panic("rethrowLevel3 中的 panic")
|
||||||
|
fmt.Printf(" rethrowLevel3 正常结束\\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
// handleHTTPRequest 处理HTTP请求
|
||||||
|
func handleHTTPRequest(path string) {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
fmt.Printf(" HTTP请求 %s 发生 panic,已恢复: %v\\n", path, r)
|
||||||
|
// 在实际应用中,这里会返回 500 错误
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
fmt.Printf(" 处理请求: %s\\n", path)
|
||||||
|
|
||||||
|
switch path {
|
||||||
|
case "/api/panic":
|
||||||
|
panic("模拟处理器 panic")
|
||||||
|
case "/api/error":
|
||||||
|
// 这里应该返回错误而不是 panic
|
||||||
|
fmt.Printf(" 请求处理出错\\n")
|
||||||
|
default:
|
||||||
|
fmt.Printf(" 请求处理成功\\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Job 任务结构
|
||||||
|
type Job struct {
|
||||||
|
ID int
|
||||||
|
Data string
|
||||||
|
}
|
||||||
|
|
||||||
|
// processJobSafely 安全处理任务
|
||||||
|
func processJobSafely(job Job) {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
fmt.Printf(" 任务 %d 处理时发生 panic,已恢复: %v\\n", job.ID, r)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
fmt.Printf(" 处理任务 %d: %s\\n", job.ID, job.Data)
|
||||||
|
|
||||||
|
switch job.Data {
|
||||||
|
case "panic任务":
|
||||||
|
panic("任务处理中的 panic")
|
||||||
|
case "error任务":
|
||||||
|
fmt.Printf(" 任务处理出错\\n")
|
||||||
|
default:
|
||||||
|
fmt.Printf(" 任务处理成功\\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseIntSafely 安全解析整数
|
||||||
|
func parseIntSafely(s string) int {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
fmt.Printf(" 解析 '%s' 时发生 panic: %v\\n", s, r)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
if s == "" {
|
||||||
|
panic("空字符串无法解析")
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := strconv.Atoi(s)
|
||||||
|
if err != nil {
|
||||||
|
return 0 // 返回默认值
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// safeFactorial 安全计算阶乘
|
||||||
|
func safeFactorial(n int) int {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
fmt.Printf(" 计算 %d! 时发生 panic: %v\\n", n, r)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return factorial(n)
|
||||||
|
}
|
||||||
|
|
||||||
|
// factorial 计算阶乘(可能 panic)
|
||||||
|
func factorial(n int) int {
|
||||||
|
if n < 0 {
|
||||||
|
panic("负数没有阶乘")
|
||||||
|
}
|
||||||
|
if n == 0 || n == 1 {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return n * factorial(n-1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// callLibraryFunction 库函数(不好的做法)
|
||||||
|
func callLibraryFunction(input string) (string, error) {
|
||||||
|
// 这个函数直接 panic,不是好的做法
|
||||||
|
if input == "invalid" {
|
||||||
|
panic("库函数中的 panic")
|
||||||
|
}
|
||||||
|
return "处理结果: " + input, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// safeLibraryFunction 安全的库函数
|
||||||
|
func safeLibraryFunction(input string) (result string, err error) {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
err = fmt.Errorf("库函数内部错误: %v", r)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// 调用可能 panic 的函数
|
||||||
|
return callLibraryFunction(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
// logPanic 记录 panic 信息
|
||||||
|
func logPanic(r interface{}) {
|
||||||
|
fmt.Printf(" [PANIC LOG] 时间: %s\\n", time.Now().Format("2006-01-02 15:04:05"))
|
||||||
|
fmt.Printf(" [PANIC LOG] 错误: %v\\n", r)
|
||||||
|
fmt.Printf(" [PANIC LOG] 类型: %T\\n", r)
|
||||||
|
|
||||||
|
// 获取调用栈
|
||||||
|
buf := make([]byte, 1024)
|
||||||
|
n := runtime.Stack(buf, false)
|
||||||
|
fmt.Printf(" [PANIC LOG] 调用栈:\\n%s\\n", buf[:n])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Server 服务器结构
|
||||||
|
type Server struct {
|
||||||
|
name string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) Start() {
|
||||||
|
fmt.Printf(" 服务器 %s 启动\\n", s.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) Stop() {
|
||||||
|
fmt.Printf(" 服务器 %s 停止\\n", s.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) handlePanic() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
fmt.Printf(" 服务器 %s 捕获 panic: %v\\n", s.name, r)
|
||||||
|
fmt.Printf(" 服务器继续运行\\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// divide 除法运算
|
||||||
|
func divide(a, b float64) (float64, error) {
|
||||||
|
if b == 0 {
|
||||||
|
return 0, fmt.Errorf("除数不能为零")
|
||||||
|
}
|
||||||
|
return a / b, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// assertPositive 断言为正数
|
||||||
|
func assertPositive(n int) {
|
||||||
|
if n <= 0 {
|
||||||
|
panic(fmt.Sprintf("断言失败: %d 不是正数", n))
|
||||||
|
}
|
||||||
|
fmt.Printf(" 断言通过: %d 是正数\\n", n)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculator 计算器
|
||||||
|
type Calculator struct{}
|
||||||
|
|
||||||
|
func (c *Calculator) Calculate(expression string) (result float64, err error) {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
err = fmt.Errorf("计算表达式 '%s' 时发生内部错误: %v", expression, r)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
switch expression {
|
||||||
|
case "10 + 5":
|
||||||
|
return 15, nil
|
||||||
|
case "invalid":
|
||||||
|
return 0, fmt.Errorf("无效的表达式: %s", expression)
|
||||||
|
case "panic":
|
||||||
|
panic("计算器内部 panic")
|
||||||
|
default:
|
||||||
|
return 0, fmt.Errorf("不支持的表达式: %s", expression)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
运行这个程序:
|
||||||
|
go run 03-panic-recover.go
|
||||||
|
|
||||||
|
学习要点:
|
||||||
|
1. panic 是运行时错误,会导致程序崩溃
|
||||||
|
2. recover 只能在 defer 函数中使用来捕获 panic
|
||||||
|
3. defer 语句按照 LIFO 顺序执行
|
||||||
|
4. panic 会沿着调用栈向上传播
|
||||||
|
5. panic/recover 应该谨慎使用,优先使用错误处理
|
||||||
|
|
||||||
|
Panic 的特性:
|
||||||
|
1. 停止当前函数的正常执行
|
||||||
|
2. 执行当前函数的所有 defer 语句
|
||||||
|
3. 向上传播到调用函数
|
||||||
|
4. 如果没有被 recover,程序会崩溃
|
||||||
|
5. 可以携带任意类型的值
|
||||||
|
|
||||||
|
Recover 的特性:
|
||||||
|
1. 只能在 defer 函数中直接调用
|
||||||
|
2. 返回 panic 的值,如果没有 panic 返回 nil
|
||||||
|
3. 只能捕获当前 goroutine 的 panic
|
||||||
|
4. 捕获后程序可以继续执行
|
||||||
|
5. 可以根据 panic 值进行不同处理
|
||||||
|
|
||||||
|
常见的 Panic 场景:
|
||||||
|
1. 数组或切片越界访问
|
||||||
|
2. 空指针解引用
|
||||||
|
3. 类型断言失败(不安全断言)
|
||||||
|
4. 向已关闭的 channel 发送数据
|
||||||
|
5. 除零操作(整数除法)
|
||||||
|
|
||||||
|
使用场景:
|
||||||
|
1. Panic 适用场景:
|
||||||
|
- 程序逻辑错误
|
||||||
|
- 不可恢复的错误
|
||||||
|
- 初始化失败
|
||||||
|
- 断言失败
|
||||||
|
|
||||||
|
2. Error 适用场景:
|
||||||
|
- 预期可能发生的错误
|
||||||
|
- 用户输入错误
|
||||||
|
- 网络或I/O错误
|
||||||
|
- 业务逻辑错误
|
||||||
|
|
||||||
|
最佳实践:
|
||||||
|
1. 优先使用错误返回值而不是 panic
|
||||||
|
2. 只在真正异常的情况下使用 panic
|
||||||
|
3. 在库代码中避免 panic,转换为错误
|
||||||
|
4. 在应用边界使用 recover 保护
|
||||||
|
5. 记录 panic 信息用于调试
|
||||||
|
6. 使用 defer 进行资源清理
|
||||||
|
|
||||||
|
注意事项:
|
||||||
|
1. recover 只能在 defer 中直接调用
|
||||||
|
2. 不要忽略 recover 的返回值
|
||||||
|
3. panic 会影响程序性能
|
||||||
|
4. 过度使用 panic/recover 会使代码难以理解
|
||||||
|
5. 在 goroutine 中的 panic 需要单独处理
|
||||||
|
|
||||||
|
实际应用:
|
||||||
|
1. Web 服务器的 panic 恢复中间件
|
||||||
|
2. 工作池中的任务 panic 处理
|
||||||
|
3. 数据解析中的异常处理
|
||||||
|
4. 递归函数的栈溢出保护
|
||||||
|
5. 库函数的 panic 转错误
|
||||||
|
|
||||||
|
性能考虑:
|
||||||
|
1. panic/recover 有一定的性能开销
|
||||||
|
2. 不应该用于正常的控制流
|
||||||
|
3. 频繁的 panic/recover 会影响性能
|
||||||
|
4. 错误处理通常比 panic/recover 更高效
|
||||||
|
5. 在性能敏感的代码中谨慎使用
|
||||||
|
*/
|
Reference in New Issue
Block a user