Go 中闭包的常见使用场景

发布于:2025-05-16 ⋅ 阅读:(22) ⋅ 点赞:(0)

在 Go 中,闭包(Closure) 是一个函数值,它引用了其定义时所在作用域中的变量。也就是说,闭包可以访问并修改外部作用域中的变量。

Go 中闭包的常见使用场景

✅ 1. 封装状态(无须结构体)

闭包可以用于封装状态,而无需显式定义结构体。

示例:计数器
func newCounter() func() int {
    var count int
    return func() int {
        count++
        return count
    }
}

counter := newCounter()
fmt.Println(counter()) // 输出 1
fmt.Println(counter()) // 输出 2

闭包捕获了 count 变量,实现了私有状态。


✅ 2. 作为回调函数或事件处理

闭包常用于注册回调函数,特别是在 HTTP 处理、定时器、goroutine 中。

示例:HTTP HandlerFunc
http.HandleFunc("/hello", func(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Hello from closure!")
})

闭包可以直接访问外部变量,比如数据库连接池、配置等。


✅ 3. 延迟执行 / defer 结合使用

闭包可以在 defer 中保存上下文状态。

示例:
for i := 0; i < 3; i++ {
    val := i
    go func() {
        fmt.Println(val)
    }()
}

如果不使用闭包传参或捕获局部变量,所有 goroutine 都会打印最后一个 i 的值。通过闭包捕获 val,确保每个 goroutine 拿到的是当前循环的值。


✅ 4. 装饰器模式 / 函数包装

闭包可用于增强函数行为,比如日志、限流、超时控制等。

示例:日志中间件
func withLog(fn func()) func() {
    return func() {
        fmt.Println("Before function call")
        fn()
        fmt.Println("After function call")
    }
}

f := withLog(func() {
    fmt.Println("Executing main logic")
})

f()

输出:

Before function call
Executing main logic
After function call

✅ 5. 惰性初始化

闭包可以用于实现单例或懒加载逻辑。

示例:
var connectOnce sync.Once
var db *sql.DB

func GetDB() *sql.DB {
    connectOnce.Do(func() {
        var err error
        db, err = sql.Open("mysql", "user:pass@/dbname")
        if err != nil {
            panic(err)
        }
    })
    return db
}

闭包中初始化数据库连接,只执行一次。


总结:闭包的典型用途

场景 描述
状态封装 实现类似类的私有状态
回调函数 HTTP Handler、事件监听
延迟执行 defer + 捕获变量
函数装饰 添加日志、限流、权限等
单例与初始化 惰性加载资源

在你的例子中,Split使用闭包来捕获 rd 变量,实现了一个线程安全的轮询分发策略,是 Go 中闭包非常典型且实用的一种写法。


网站公告

今日签到

点亮在社区的每一天
去签到