一切皆有可能:Golang中的“ThreadLocal”库

一切皆有可能:Golang中的“ThreadLocal”库

本文介绍的是新写的 routine 库,它封装并提供了一些易用、高性能的 goroutine 上下文访问接口,可以帮助你更优雅地访问协程上下文信息,但你也可能就此打开了潘多拉魔盒。

介绍

Golang 语言从设计之初,就一直在不遗余力地向开发者屏蔽协程上下文的概念,包括协程 goid 的获取、进程内部协程状态、协程上下文存储等。

如果你使用过其他语言如 C++/Java 等,那么你一定很熟悉 ThreadLocal ,而在开始使用 Golang 之后,你一定会为缺少类似 ThreadLocal 的便捷功能而深感困惑与苦恼。当然你可以选择使用 Context ,让它携带着全部上下文信息,在所有函数的第一个输入参数中出现,然后在你的系统中到处穿梭。

routine 的核心目标就是开辟另一条路:将 goroutine local storage 引入 Golang 世界,同时也将协程信息暴露出来,以满足某些人可能有的需求。

使用演示

此章节简要介绍如何安装与使用 routine 库。

安装

go get github.com/go-eden/routine

使用 goid

以下代码简单演示了 routine.Goid() routine.AllGoids() 的使用:

package main
import (
 "fmt"
 "github.com/go-eden/routine"
 "time"
func main() {
 go func() {
 time.Sleep(time.Second)
 goid := routine.Goid()
 goids := routine.AllGoids()
 fmt.Printf("curr goid: %d\n", goid)
 fmt.Printf("all goids: %v\n", goids)

此例中 main 函数启动了一个新的协程,因此 Goid() 返回了主协程 1 AllGoids() 返回了主协程及协程 18 :

curr goid: 1
all goids: [1 18]

使用 LocalStorage

以下代码简单演示了 LocalStorage 的创建、设置、获取、跨协程传播等:

package main
import (
 "fmt"
 "github.com/go-eden/routine"
 "time"
var nameVar = routine.NewLocalStorage()
func main() {
 nameVar.Set("hello world")
 fmt.Println("name: ", nameVar.Get())
 // 其他协程不能读取前面Set的"hello world"
 go func() {
 fmt.Println("name1: ", nameVar.Get())
 // 但是可以通过Go函数启动新协程,并将当前main协程的全部协程上下文变量赋值过去
 routine.Go(func() {
 fmt.Println("name2: ", nameVar.Get())
 // 或者,你也可以手动copy当前协程上下文至新协程,Go()函数的内部实现也是如此
 ic := routine.BackupContext()
 go func() {
 routine.InheritContext(ic)
 fmt.Println("name3: ", nameVar.Get())
 time.Sleep(time.Second)