开源仓库: https://github.com/go-eden/ro...
本文介绍的是新写的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 mainimport ( "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: 1all goids: [1 18]
应用LocalStorage
以下代码简略演示了LocalStorage
的创立、设置、获取、跨协程流传等:
package mainimport ( "fmt" "github.com/go-eden/routine" "time")var nameVar = routine.NewLocalStorage()func main() { nameVar.Set("hello world") fmt.Println("name: ", nameVar.Get()) // other goroutine cannot read nameVar go func() { fmt.Println("name1: ", nameVar.Get()) }() // but, the new goroutine could inherit/copy all local data from the current goroutine like this: routine.Go(func() { fmt.Println("name2: ", nameVar.Get()) }) // or, you could copy all local data manually ic := routine.BackupContext() go func() { routine.InheritContext(ic) fmt.Println("name3: ", nameVar.Get()) }() time.Sleep(time.Second)}
执行后果为:
name: hello worldname1: <nil>name3: hello worldname2: hello world
API文档
此章节具体介绍了routine
库封装的全副接口,以及它们的外围性能、实现形式等。
Goid() (id int64)
获取以后goroutine
的goid
。
在失常状况下,Goid()
优先尝试通过go_tls
的形式间接获取,此操作极快,耗时通常只相当于rand.Int()
的五分之一。
若呈现版本不兼容等谬误时,Goid()
会尝试从runtime.Stack
信息中解析获取,此时性能会呈现指数级的损耗,即变慢约一千倍,但能够保障性能失常可用。
AllGoids() (ids []int64)
获取以后过程全副沉闷goroutine
的goid
。
在go 1.15
及更旧的版本中,AllGoids()
会尝试从runtime.Stack
信息中解析获取全副协程信息,但此操作十分低效,十分不倡议在高频逻辑中应用。
在go 1.16
之后的版本中,AllGoids()
会通过native
的形式间接读取runtime
的全局协程池信息,在性能上失去了极大的进步, 但思考到生产环境中可能有万、百万级的协程数量,因而仍不倡议在高频应用它。
NewLocalStorage()
:
创立一个新的LocalStorage
实例,它的设计思路与用法和其余语言中的ThreadLocal
十分类似。
BackupContext() *ImmutableContext
备份以后协程上下文的local storage
数据,它只是一个便于上下文数据传递的不可变构造体。
InheritContext(ic *ImmutableContext)
被动继承备份到的上下文local storage
数据,它会将其余协程BackupContext()
的数据复制入以后协程上下文中,从而反对跨协程的上下文数据流传。
Go(f func())
启动一个新的协程,同时主动将以后协程的全副上下文local storage
数据复制至新协程,它的外部实现由BackupContext()
和InheritContext()
组成。
LocalStorage
示意协程上下文变量,反对的函数包含:
Get() (value interface{})
:获取以后协程已设置的变量值,若未设置则为nil
Set(v interface{}) interface{}
:设置以后协程的上下文变量值,返回之前已设置的旧值Del() (v interface{})
:删除以后协程的上下文变量值,返回已删除的旧值Clear()
:彻底清理此上下文变量在所有协程中保留的旧值
垃圾回收
routine
库外部保护了全局的storages
,它存储了全副协程的全副变量值,在读写时基于goroutine
的goid
和LocalStorage
进行数据惟一映射。
在过程的整个生命周期中,可能呈现有无数个协程的创立与销毁,
因而有必要被动清理dead
协程在全局storages
中缓存的上下文数据。
这个工作由routine
库中的一个全局定时器执行,它会在必要的时候,
每隔一段时间扫描并清理dead
协程的相干信息,以防止可能呈现的内存泄露隐患。
License
MIT