golang优雅访问协程上下文信息的高性能插件库routine的使用
Golang优雅访问协程上下文信息的高性能插件库routine的使用
routine
库封装并提供了一些易于使用、非竞争、高性能的goroutine
上下文访问接口,可以帮助您更优雅地访问协程上下文信息。
介绍
Golang语言从一开始设计就极力避免开发者接触协程上下文的概念,包括获取协程goid
、协程在进程中的状态、协程上下文的存储等。routine
的核心目标是为Golang世界引入goroutine local storage
。
安装
go get github.com/timandy/routine
使用示例
获取协程ID(goid)
package main
import (
"fmt"
"time"
"github.com/timandy/routine"
)
func main() {
goid := routine.Goid()
fmt.Printf("当前协程ID: %v\n", goid)
go func() {
goid := routine.Goid()
fmt.Printf("子协程ID: %v\n", goid)
}()
// 等待子协程执行完成
time.Sleep(time.Second)
}
输出结果:
当前协程ID: 1
子协程ID: 6
使用ThreadLocal
package main
import (
"fmt"
"time"
"github.com/timandy/routine"
)
var threadLocal = routine.NewThreadLocal[string]()
var inheritableThreadLocal = routine.NewInheritableThreadLocal[string]()
func main() {
threadLocal.Set("hello world")
inheritableThreadLocal.Set("Hello world2")
fmt.Println("threadLocal:", threadLocal.Get())
fmt.Println("inheritableThreadLocal:", inheritableThreadLocal.Get())
// 子协程无法读取之前赋值的"hello world"
go func() {
fmt.Println("goroutine中的threadLocal:", threadLocal.Get())
fmt.Println("goroutine中的inheritableThreadLocal:", inheritableThreadLocal.Get())
}()
// 通过Go/GoWait/GoWaitResult函数启动新的子协程,可以自动传递当前协程的所有可继承变量
routine.Go(func() {
fmt.Println("通过Go启动的goroutine中的threadLocal:", threadLocal.Get())
fmt.Println("通过Go启动的goroutine中的inheritableThreadLocal:", inheritableThreadLocal.Get())
})
// 也可以通过WrapTask/WrapWaitTask/WrapWaitResultTask函数创建任务,自动捕获当前协程的所有可继承变量
task := routine.WrapTask(func() {
fmt.Println("通过WrapTask创建的任务中的threadLocal:", threadLocal.Get())
fmt.Println("通过WrapTask创建的任务中的inheritableThreadLocal:", inheritableThreadLocal.Get())
})
go task.Run()
// 等待子协程执行完成
time.Sleep(time.Second)
}
输出结果:
threadLocal: hello world
inheritableThreadLocal: Hello world2
goroutine中的threadLocal:
goroutine中的inheritableThreadLocal:
通过Go启动的goroutine中的threadLocal:
通过Go启动的goroutine中的inheritableThreadLocal: Hello world2
通过WrapTask创建的任务中的threadLocal:
通过WrapTask创建的任务中的inheritableThreadLocal: Hello world2
主要API
Goid() uint64
获取当前goroutine的ID
NewThreadLocalT any ThreadLocal[T]
创建新的ThreadLocal实例,初始值为类型T的零值
NewThreadLocalWithInitial[T any](supplier Supplier[T]) ThreadLocal[T]
创建新的ThreadLocal实例,初始值为supplier()方法的返回值
NewInheritableThreadLocalT any ThreadLocal[T]
创建新的可继承ThreadLocal实例,初始值为类型T的零值
NewInheritableThreadLocalWithInitial[T any](supplier Supplier[T]) ThreadLocal[T]
创建新的可继承ThreadLocal实例,初始值为supplier()方法的返回值
WrapTask(fun Runnable) FutureTask[any]
创建新任务并捕获当前goroutine的inheritableThreadLocals
WrapWaitTask(fun CancelRunnable) FutureTask[any]
创建新任务并捕获当前goroutine的inheritableThreadLocals
WrapWaitResultTask[TResult any](fun CancelCallable[TResult]) FutureTask[TResult]
创建新任务并捕获当前goroutine的inheritableThreadLocals
Go(fun Runnable)
启动新协程并自动复制当前协程的所有上下文inheritableThreadLocals数据
GoWait(fun CancelRunnable) FutureTask[any]
启动新协程并自动复制当前协程的所有上下文inheritableThreadLocals数据
GoWaitResult[TResult any](fun CancelCallable[TResult]) FutureTask[TResult]
启动新协程并自动复制当前协程的所有上下文inheritableThreadLocals数据
垃圾回收
routine
为每个协程分配一个thread
结构,存储与协程相关的上下文变量信息。当协程执行完成并退出时,g.labels
将被设置为nil,不再引用thread
结构。thread
结构将在下一次GC时被回收。
更多关于golang优雅访问协程上下文信息的高性能插件库routine的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html
更多关于golang优雅访问协程上下文信息的高性能插件库routine的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
Golang优雅访问协程上下文信息的高性能插件库routine
routine是一个专门为Go语言设计的高性能协程上下文管理库,它提供了优雅的方式来存储和访问协程(goroutine)本地存储(类似于线程本地存储TLS)。
主要特性
- 高性能:基于Go的协程ID实现,比基于context.Context的方式性能更高
- 易用性:提供简单直观的API来管理协程本地存储
- 类型安全:通过泛型支持类型安全的存储访问
- 无侵入性:不需要修改现有代码结构
安装
go get github.com/timandy/routine
基本使用示例
package main
import (
"fmt"
"time"
"github.com/timandy/routine"
)
func main() {
// 创建一个协程本地存储
var local = routine.NewLocal[string]()
// 在主协程设置值
local.Set("main goroutine value")
// 启动新协程
go func() {
// 在新协程设置不同的值
local.Set("child goroutine value")
// 获取当前协程的值
val := local.Get()
fmt.Println("child goroutine:", val)
}()
// 主协程获取值
val := local.Get()
fmt.Println("main goroutine:", val)
// 等待子协程完成
time.Sleep(time.Second)
}
高级用法
1. 默认值支持
var local = routine.NewLocalWithDefault("default value")
func example() {
// 未设置值时返回默认值
fmt.Println(local.Get()) // 输出: default value
local.Set("new value")
fmt.Println(local.Get()) // 输出: new value
local.Remove()
fmt.Println(local.Get()) // 输出: default value
}
2. 跨协程继承
var local = routine.NewLocal[int]()
func parent() {
local.Set(42)
// 继承当前协程的上下文
go routine.Go(func() {
fmt.Println("inherited value:", local.Get()) // 输出: 42
})
// 不继承上下文
go func() {
fmt.Println("non-inherited value:", local.Get()) // 输出: 0
}()
}
3. 批量操作
func batchExample() {
var (
local1 = routine.NewLocal[string]()
local2 = routine.NewLocal[int]()
)
// 批量设置值
routine.Set(local1, "hello", local2, 100)
// 批量获取值
v1, v2 := routine.Get(local1, local2)
fmt.Println(v1, v2) // 输出: hello 100
}
性能优化技巧
- 重用Local对象:避免频繁创建和销毁Local对象
- 减少Get/Set调用:在热点路径上缓存获取的值
- 使用批量操作:当需要操作多个Local时,使用批量API减少锁竞争
与标准库context.Context对比
特性 | routine | context.Context |
---|---|---|
性能 | 高 | 低 |
使用便捷性 | 简单 | 复杂 |
跨协程继承 | 支持 | 不支持 |
类型安全 | 是 | 否 |
标准库支持 | 否 | 是 |
实际应用场景
- 请求跟踪:在微服务中传递请求ID
- 日志字段:存储当前请求的特定日志字段
- 数据库事务:管理当前协程的事务上下文
- 用户会话:存储当前用户的认证信息
注意事项
- routine不适用于大量短期协程的场景,因为会有内存泄漏风险
- 在协程池等复用协程的场景中,记得清理Local存储
- 对于极高性能要求的场景,建议进行基准测试
routine库为Go程序提供了一种高效、类型安全的方式来管理协程本地存储,特别适合需要在协程间隔离但又不想传递大量参数的场景。