Go 中闭包的常见使用场景
在 Go 中,闭包(Closure) 是一个函数值,它引用了其定义时所在作用域中的变量。也就是说,闭包可以访问并修改外部作用域中的变量。
Go 中闭包的常见使用场景
✅ 1. 封装状态(无须结构体)
闭包可以用于封装状态,而无需显式定义结构体。
示例:计数器
func newCounter() func() int {var count intreturn 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 := igo 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.DBfunc GetDB() *sql.DB {connectOnce.Do(func() {var err errordb, err = sql.Open("mysql", "user:pass@/dbname")if err != nil {panic(err)}})return db
}
闭包中初始化数据库连接,只执行一次。
总结:闭包的典型用途
场景 | 描述 |
---|---|
状态封装 | 实现类似类的私有状态 |
回调函数 | HTTP Handler、事件监听 |
延迟执行 | defer + 捕获变量 |
函数装饰 | 添加日志、限流、权限等 |
单例与初始化 | 惰性加载资源 |
在你的例子中,Split使用闭包来捕获 rd
变量,实现了一个线程安全的轮询分发策略,是 Go 中闭包非常典型且实用的一种写法。