1716 字
9 分钟
Go 高级特性

Go 高级特性#

一、错误处理机制#

1.1 defer机制#

  • defer 的作用

    • 用来指定在函数返回前执行的代码
    • 主要用于资源释放(文件关闭、锁释放、数据库连接关闭)
    • 配合recover捕获panic
  • defer 的执行顺序

    • LIFO(后进先出)顺序执行
    • 多个defer形成defer栈
    • 函数返回时从栈顶开始执行
  • defer底层实现

    • Go 1.12及之前:所有defer在堆上分配
    • Go 1.13:引入栈上分配,提升性能
    • Go 1.14:引入开放编码(open-coded),进一步优化
type _defer struct {
siz int32 // 参数和返回值的内存大小
started bool // defer是否已经开始执行
heap bool // 是否分配在堆上
openDefer bool // 是否使用开放编码优化(Go 1.14+)
sp uintptr // 栈指针,记录defer语句时的栈位置
pc uintptr // 程序计数器,defer函数的返回地址
fn *funcval // defer延迟调用的函数
_panic *_panic // 触发defer的panic结构体
link *_defer // 链接到下一个defer,形成链表
}
  • defer陷阱
    // 陷阱1:循环中的defer
    for i := 0; i < 1000; i++ {
    defer file.Close() // 错误:defer在函数返回时才执行
    }
    // 陷阱2:defer参数即时求值
    func a() {
    i := 0
    defer fmt.Println(i) // 输出0,不是1
    i++
    }
    // 陷阱3:defer与返回值
    func foo() (result int) {
    defer func() {
    result++ // 可以修改命名返回值
    }()
    return 0 // 最终返回1
    }

1.2 panic和recover#

  • panic机制

    • Go的运行时异常机制
    • 未被recover捕获会导致程序崩溃
    • 输出堆栈信息
  • panic触发方式

    • 主动调用:panic("error message")
    • 运行时错误:
      • 空指针解引用
      • 数组/切片越界
      • 类型断言失败
      • 向已关闭的channel发送数据
      • 并发map读写
  • panic执行流程

    1. 停止当前函数的正常执行
    2. 执行当前函数的所有defer(LIFO顺序)
    3. 返回调用者,重复步骤1-2
    4. 直到被recover捕获或程序终止
  • recover使用规则

    • 必须在defer函数中直接调用
    • 只能捕获当前goroutine的panic
    • recover后从defer之后继续执行
    • 返回panic的值
func example() {
defer func() {
if r := recover(); r != nil {
fmt.Println("Recovered:", r)
}
}()
panic("something went wrong")
fmt.Println("This won't execute")
}

二、并发通信#

2.1 channel基础#

  • channel的作用

    • goroutine之间通信
    • 同步goroutine执行
    • 实现CSP并发模型
  • channel底层结构

type hchan struct {
qcount uint // 当前缓冲区中的元素数量
dataqsiz uint // 缓冲区的容量
buf unsafe.Pointer // 指向环形缓冲区
elemsize uint16 // 元素大小
closed uint32 // 关闭标志
elemtype *_type // 元素类型
sendx uint // 发送索引
recvx uint // 接收索引
recvq waitq // 等待接收的goroutine队列
sendq waitq // 等待发送的goroutine队列
lock mutex // 互斥锁
}
  • 发送和接收流程
    • 发送:加锁 → 检查是否关闭 → 尝试直接发送给接收者 → 写入缓冲区 → 加入sendq等待
    • 接收:加锁 → 尝试从缓冲区读取 → 尝试从发送者接收 → 加入recvq等待

2.2 channel分类#

  • 按方向分类

    var ch1 chan int // 双向channel
    var ch2 <-chan int // 只读channel
    var ch3 chan<- int // 只写channel
    // 双向可以转换为单向
    ch1 = make(chan int)
    ch2 = ch1 // 双向转只读
    ch3 = ch1 // 双向转只写
  • 按缓冲分类

    • 无缓冲channel:同步通信,发送和接收必须同时准备好
    // 死锁示例
    func main() {
    ch := make(chan int)
    ch <- 42 // 阻塞,等待接收者
    <-ch // 永远执行不到
    }
    // 正确用法
    func main() {
    ch := make(chan int)
    go func() {
    ch <- 42
    }()
    fmt.Println(<-ch)
    }
    • 有缓冲channel:异步通信,缓冲区满时发送阻塞,空时接收阻塞

2.3 channel操作#

  • 关闭channel的影响

    • 向已关闭的channel发送:panic
    • 从已关闭的channel接收:返回零值和false
    • 重复关闭channel:panic
    • 关闭nil channel:panic
  • 判断channel是否关闭

    // 方法1:双返回值
    value, ok := <-ch
    if !ok {
    fmt.Println("Channel closed")
    }
    // 方法2:for range(推荐)
    for value := range ch {
    fmt.Println(value)
    }
    // channel关闭后自动退出
    // 方法3:select with default
    select {
    case v, ok := <-ch:
    if !ok {
    fmt.Println("Channel closed")
    }
    default:
    fmt.Println("No data available")
    }

三、上下文管理#

3.1 context接口#

type Context interface {
// Done返回一个channel,当context被取消或超时时关闭
Done() <-chan struct{}
// Err返回context结束的原因
// context.Canceled:被取消
// context.DeadlineExceeded:超时
Err() error
// Deadline返回context的截止时间
// ok=false表示没有设置deadline
Deadline() (deadline time.Time, ok bool)
// Value返回context中的键值对
Value(key interface{}) interface{}
}

3.2 context类型#

  • context.Background():根context,通常用于main函数
  • context.TODO():不确定使用哪个context时的占位符
  • context.WithCancel():可取消的context
  • context.WithDeadline():带截止时间的context
  • context.WithTimeout():带超时的context
  • context.WithValue():携带键值对的context

3.3 context最佳实践#

// 1. context应该作为第一个参数
func DoSomething(ctx context.Context, arg string) error
// 2. 不要存储context
type Server struct {
// ctx context.Context // 错误
}
// 3. 传递请求范围的值,不传递可选参数
ctx = context.WithValue(ctx, "requestID", "12345") // 正确
// ctx = context.WithValue(ctx, "dbConfig", config) // 错误
// 4. 使用context控制goroutine生命周期
func worker(ctx context.Context) {
for {
select {
case <-ctx.Done():
return // 优雅退出
default:
// 执行工作
}
}
}

四、控制流#

4.1 switch语句#

  • 基本特性

    • 不需要break,自动终止
    • case可以是多个值:case 1, 2, 3:
    • case表达式运行时求值
    • switch后无表达式相当于switch true
  • fallthrough

    • 强制执行下一个case(不判断条件)
    • 不能用在最后一个case
    • 不能用在类型switch中
  • 类型switch

    func typeSwitch(x interface{}) {
    switch v := x.(type) {
    case int:
    fmt.Printf("int: %d\n", v)
    case string:
    fmt.Printf("string: %s\n", v)
    case []int:
    fmt.Printf("[]int: %v\n", v)
    default:
    fmt.Printf("unknown type: %T\n", v)
    }
    }

五、面向对象特性#

5.1 Go的面向对象#

  • Go没有类和继承

    • 使用结构体代替类
    • 使用组合代替继承
    • 使用接口实现多态
  • 方法接收者

    // 值接收者
    func (p Point) Distance() float64 {
    // 不能修改p
    }
    // 指针接收者
    func (p *Point) Move(dx, dy float64) {
    // 可以修改p
    }
  • 组合实现”继承”

    type Animal struct {
    Name string
    }
    func (a Animal) Speak() {
    fmt.Println("...")
    }
    type Dog struct {
    Animal // 嵌入
    Breed string
    }
    // Dog"继承"了Animal的Speak方法
    // 可以"重写"(遮蔽)
    func (d Dog) Speak() {
    fmt.Println("Woof!")
    }
  • 接口实现多态

    type Speaker interface {
    Speak()
    }
    // Dog和Cat都实现Speaker接口
    func MakeSound(s Speaker) {
    s.Speak() // 多态调用
    }

六、反射机制#

6.1 反射基础#

  • reflect.Type:类型信息
  • reflect.Value:值信息
  • reflect.TypeOf():获取类型
  • reflect.ValueOf():获取值

6.2 反射三定律#

  1. 反射可以从接口值得到反射对象
  2. 反射可以从反射对象得到接口值
  3. 要修改反射对象,值必须可设置(CanSet)

6.3 反射应用#

// 获取结构体字段信息
func inspectStruct(s interface{}) {
t := reflect.TypeOf(s)
v := reflect.ValueOf(s)
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
value := v.Field(i)
fmt.Printf("%s: %v (tag: %s)\n",
field.Name, value, field.Tag)
}
}
// 动态调用方法
func callMethod(obj interface{}, method string) {
v := reflect.ValueOf(obj)
m := v.MethodByName(method)
if m.IsValid() {
m.Call(nil)
}
}
Go 高级特性
https://fuwari.vercel.app/posts/go_advanced/
作者
Jarrett
发布于
2025-08-13
许可协议
CC BY-NC-SA 4.0