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

1 回复

更多关于golang优雅访问协程上下文信息的高性能插件库routine的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


Golang优雅访问协程上下文信息的高性能插件库routine

routine是一个专门为Go语言设计的高性能协程上下文管理库,它提供了优雅的方式来存储和访问协程(goroutine)本地存储(类似于线程本地存储TLS)。

主要特性

  1. 高性能:基于Go的协程ID实现,比基于context.Context的方式性能更高
  2. 易用性:提供简单直观的API来管理协程本地存储
  3. 类型安全:通过泛型支持类型安全的存储访问
  4. 无侵入性:不需要修改现有代码结构

安装

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
}

性能优化技巧

  1. 重用Local对象:避免频繁创建和销毁Local对象
  2. 减少Get/Set调用:在热点路径上缓存获取的值
  3. 使用批量操作:当需要操作多个Local时,使用批量API减少锁竞争

与标准库context.Context对比

特性 routine context.Context
性能
使用便捷性 简单 复杂
跨协程继承 支持 不支持
类型安全
标准库支持

实际应用场景

  1. 请求跟踪:在微服务中传递请求ID
  2. 日志字段:存储当前请求的特定日志字段
  3. 数据库事务:管理当前协程的事务上下文
  4. 用户会话:存储当前用户的认证信息

注意事项

  1. routine不适用于大量短期协程的场景,因为会有内存泄漏风险
  2. 在协程池等复用协程的场景中,记得清理Local存储
  3. 对于极高性能要求的场景,建议进行基准测试

routine库为Go程序提供了一种高效、类型安全的方式来管理协程本地存储,特别适合需要在协程间隔离但又不想传递大量参数的场景。

回到顶部