github.com/xg0n/routine@v0.0.0-20240119033701-c364deb94aee/README_zh.md (about) 1 # routine 2 3 [![Build Status](https://github.com/timandy/routine/actions/workflows/build.yml/badge.svg)](https://github.com/timandy/routine/actions) 4 [![Codecov](https://codecov.io/gh/timandy/routine/branch/main/graph/badge.svg)](https://app.codecov.io/gh/timandy/routine) 5 [![Go Report Card](https://goreportcard.com/badge/github.com/timandy/routine)](https://goreportcard.com/report/github.com/timandy/routine) 6 [![Documentation](https://pkg.go.dev/badge/github.com/timandy/routine.svg)](https://pkg.go.dev/github.com/timandy/routine) 7 [![Release](https://img.shields.io/github/release/timandy/routine.svg)](https://github.com/timandy/routine/releases) 8 [![License](https://img.shields.io/github/license/timandy/routine.svg)](https://github.com/timandy/routine/blob/main/LICENSE) 9 10 > [English Version](README.md) 11 12 `routine`封装并提供了一些易用、无竞争、高性能的`goroutine`上下文访问接口,它可以帮助你更优雅地访问协程上下文信息。 13 14 # 介绍 15 16 `Golang`语言从设计之初,就一直在不遗余力地向开发者屏蔽协程上下文的概念,包括协程`goid`的获取、进程内部协程状态、协程上下文存储等。 17 18 如果你使用过其他语言如`C++`、`Java`等,那么你一定很熟悉`ThreadLocal`,而在开始使用`Golang`之后,你一定会为缺少类似`ThreadLocal`的便捷功能而深感困惑与苦恼。 19 20 当然你可以选择使用`Context`,让它携带着全部上下文信息,在所有函数的第一个输入参数中出现,然后在你的系统中到处穿梭。 21 22 而`routine`的核心目标就是开辟另一条路:将`goroutine local storage`引入`Golang`世界。 23 24 # 使用演示 25 26 此章节简要介绍如何安装与使用`routine`库。 27 28 ## 安装 29 30 ```bash 31 go get github.com/timandy/routine 32 ``` 33 34 ## 使用`goid` 35 36 以下代码简单演示了`routine.Goid()`的使用: 37 38 ```go 39 package main 40 41 import ( 42 "fmt" 43 "time" 44 45 "github.com/timandy/routine" 46 ) 47 48 func main() { 49 goid := routine.Goid() 50 fmt.Printf("cur goid: %v\n", goid) 51 go func() { 52 goid := routine.Goid() 53 fmt.Printf("sub goid: %v\n", goid) 54 }() 55 56 // 等待子协程执行完。 57 time.Sleep(time.Second) 58 } 59 ``` 60 61 此例中`main`函数启动了一个新的协程,因此`Goid()`返回了主协程`1`和子协程`6`: 62 63 ```text 64 cur goid: 1 65 sub goid: 6 66 ``` 67 68 ## 使用`ThreadLocal` 69 70 以下代码简单演示了`ThreadLocal`的创建、设置、获取、跨协程传播等: 71 72 ```go 73 package main 74 75 import ( 76 "fmt" 77 "time" 78 79 "github.com/timandy/routine" 80 ) 81 82 var threadLocal = routine.NewThreadLocal[string]() 83 var inheritableThreadLocal = routine.NewInheritableThreadLocal[string]() 84 85 func main() { 86 threadLocal.Set("hello world") 87 inheritableThreadLocal.Set("Hello world2") 88 fmt.Println("threadLocal:", threadLocal.Get()) 89 fmt.Println("inheritableThreadLocal:", inheritableThreadLocal.Get()) 90 91 // 子协程无法读取之前赋值的“hello world”。 92 go func() { 93 fmt.Println("threadLocal in goroutine:", threadLocal.Get()) 94 fmt.Println("inheritableThreadLocal in goroutine:", inheritableThreadLocal.Get()) 95 }() 96 97 // 但是,可以通过 Go/GoWait/GoWaitResult 函数启动一个新的子协程,当前协程的所有可继承变量都可以自动传递。 98 routine.Go(func() { 99 fmt.Println("threadLocal in goroutine by Go:", threadLocal.Get()) 100 fmt.Println("inheritableThreadLocal in goroutine by Go:", inheritableThreadLocal.Get()) 101 }) 102 103 // 也可以通过 WrapTask/WrapWaitTask/WrapWaitResultTask 函数创建一个任务,当前协程的所有可继承变量都可以被自动捕获。 104 task := routine.WrapTask(func() { 105 fmt.Println("threadLocal in task by WrapTask:", threadLocal.Get()) 106 fmt.Println("inheritableThreadLocal in task by WrapTask:", inheritableThreadLocal.Get()) 107 }) 108 go task.Run() 109 110 // 等待子协程执行完。 111 time.Sleep(time.Second) 112 } 113 ``` 114 115 执行结果为: 116 117 ```text 118 threadLocal: hello world 119 inheritableThreadLocal: Hello world2 120 threadLocal in goroutine: 121 inheritableThreadLocal in goroutine: 122 threadLocal in goroutine by Go: 123 inheritableThreadLocal in goroutine by Go: Hello world2 124 threadLocal in task by WrapTask: 125 inheritableThreadLocal in task by WrapTask: Hello world2 126 ``` 127 128 # API文档 129 130 此章节详细介绍了`routine`库封装的全部接口,以及它们的核心功能、实现方式等。 131 132 ## `Goid() int64` 133 134 获取当前`goroutine`的`goid`。 135 136 在`386`、`amd64`、`armv6`、`armv7`、`arm64`、`loong64`、`mips`、`mipsle`、`mips64`、`mips64le`、`ppc64`、`ppc64le`、`riscv64`、`s390x`、`wasm`架构下通过汇编代码直接获取,此操作性能极高,耗时通常只相当于`rand.Int()`的五分之一。 137 138 ## `NewThreadLocal[T any]() ThreadLocal[T]` 139 140 创建一个新的`ThreadLocal[T]`实例,其存储的初始值为类型`T`的默认值。 141 142 ## `NewThreadLocalWithInitial[T any](supplier Supplier[T]) ThreadLocal[T]` 143 144 创建一个新的`ThreadLocal[T]`实例,其存储的初始值为方法`supplier()`的返回值。 145 146 ## `NewInheritableThreadLocal[T any]() ThreadLocal[T]` 147 148 创建一个新的`ThreadLocal[T]`实例,其存储的初始值为类型`T`的默认值。 149 当通过`Go()`、`GoWait()`或`GoWaitResult()`启动新协程时,当前协程的值会被复制到新协程。 150 当通过`WrapTask()`、`WrapWaitTask()`或`WrapWaitResultTask()`创建任务时,当前协程的值会被捕获。 151 152 ## `NewInheritableThreadLocalWithInitial[T any](supplier Supplier[T]) ThreadLocal[T]` 153 154 创建一个新的`ThreadLocal[T]`实例,其存储的初始值为方法`supplier()`的返回值。 155 当通过`Go()`、`GoWait()`或`GoWaitResult()`启动新协程时,当前协程的值会被复制到新协程。 156 当通过`WrapTask()`、`WrapWaitTask()`或`WrapWaitResultTask()`创建任务时,当前协程的值会被捕获。 157 158 ## `WrapTask(fun Runnable) FutureTask[any]` 159 160 创建一个新任务,并捕获当前协程的`inheritableThreadLocals`。 161 此函数返回一个`FutureTask`实例,但返回的任务不会自动运行。 162 你可以通过`FutureTask.Run()`方法在子协程或协程池中运行它,通过`FutureTask.Get()`或`FutureTask.GetWithTimeout()`方法等待任务执行完毕。 163 任务执行时的任何`panic`都会被捕获并打印错误堆栈,在调用`FutureTask.Get()`或`FutureTask.GetWithTimeout()`方法时`panic`会被再次抛出。 164 165 ## `WrapWaitTask(fun CancelRunnable) FutureTask[any]` 166 167 创建一个新任务,并捕获当前协程的`inheritableThreadLocals`。 168 此函数返回一个`FutureTask`实例,但返回的任务不会自动运行。 169 你可以通过`FutureTask.Run()`方法在子协程或协程池中运行它,通过`FutureTask.Get()`或`FutureTask.GetWithTimeout()`方法等待任务执行完毕。 170 任务执行时的任何`panic`都会被捕获,在调用`FutureTask.Get()`或`FutureTask.GetWithTimeout()`方法时`panic`会被再次抛出。 171 172 ## `WrapWaitResultTask[TResult any](fun CancelCallable[TResult]) FutureTask[TResult]` 173 174 创建一个新任务,并捕获当前协程的`inheritableThreadLocals`。 175 此函数返回一个`FutureTask`实例,但返回的任务不会自动运行。 176 你可以通过`FutureTask.Run()`方法在子协程或协程池中运行它,通过`FutureTask.Get()`或`FutureTask.GetWithTimeout()`方法等待任务执行完毕并获取结果。 177 任务执行时的任何`panic`都会被捕获,在调用`FutureTask.Get()`或`FutureTask.GetWithTimeout()`方法时`panic`会被再次抛出。 178 179 ## `Go(fun Runnable)` 180 181 启动一个新的协程,同时自动将当前协程的全部上下文`inheritableThreadLocals`数据复制至新协程。 182 子协程执行时的任何`panic`都会被捕获并自动打印堆栈。 183 184 ## `GoWait(fun CancelRunnable) FutureTask[any]` 185 186 启动一个新的协程,同时自动将当前协程的全部上下文`inheritableThreadLocals`数据复制至新协程。 187 可以通过返回值的`FutureTask.Get()`或`FutureTask.GetWithTimeout()`方法等待子协程执行完毕。 188 子协程执行时的任何`panic`都会被捕获并在调用`FutureTask.Get()`或`FutureTask.GetWithTimeout()`时再次抛出。 189 190 ## `GoWaitResult[TResult any](fun CancelCallable[TResult]) FutureTask[TResult]` 191 192 启动一个新的协程,同时自动将当前协程的全部上下文`inheritableThreadLocals`数据复制至新协程。 193 可以通过返回值的`FutureTask.Get()`或`FutureTask.GetWithTimeout()`方法等待子协程执行完毕并获取返回值。 194 子协程执行时的任何`panic`都会被捕获并在调用`FutureTask.Get()`或`FutureTask.GetWithTimeout()`时再次抛出。 195 196 [更多API文档](https://pkg.go.dev/github.com/timandy/routine#section-documentation) 197 198 # 垃圾回收 199 200 `routine`为每个协程分配了一个`thread`结构,它存储了协程相关的上下文变量信息。 201 202 指向该结构的指针存储在协程结构的`g.labels`字段上。 203 204 当协程执行完毕退出时,`g.labels`将被设置为`nil`,不再引用`thread`结构。 205 206 `thread`结构将在下次`GC`时被回收。 207 208 如果`thread`中存储的数据也没有额外被引用,这些数据将被一并回收。 209 210 # 支持网格 211 212 | | **`darwin`** | **`linux`** | **`windows`** | **`freebsd`** | **`js`** | | 213 |---------------:|:------------:|:-----------:|:-------------:|:-------------:|:--------:|:---------------| 214 | **`386`** | | ✅ | ✅ | ✅ | | **`386`** | 215 | **`amd64`** | ✅ | ✅ | ✅ | ✅ | | **`amd64`** | 216 | **`armv6`** | | ✅ | | | | **`armv6`** | 217 | **`armv7`** | | ✅ | | | | **`armv7`** | 218 | **`arm64`** | ✅ | ✅ | | | | **`arm64`** | 219 | **`loong64`** | | ✅ | | | | **`loong64`** | 220 | **`mips`** | | ✅ | | | | **`mips`** | 221 | **`mipsle`** | | ✅ | | | | **`mipsle`** | 222 | **`mips64`** | | ✅ | | | | **`mips64`** | 223 | **`mips64le`** | | ✅ | | | | **`mips64le`** | 224 | **`ppc64`** | | ✅ | | | | **`ppc64`** | 225 | **`ppc64le`** | | ✅ | | | | **`ppc64le`** | 226 | **`riscv64`** | | ✅ | | | | **`riscv64`** | 227 | **`s390x`** | | ✅ | | | | **`s390x`** | 228 | **`wasm`** | | | | | ✅ | **`wasm`** | 229 | | **`darwin`** | **`linux`** | **`windows`** | **`freebsd`** | **`js`** | | 230 231 ✅:支持 232 233 # 鸣谢 234 235 感谢所有[贡献者](https://github.com/timandy/routine/graphs/contributors)的贡献! 236 237 # *许可证* 238 239 `routine`是在 [Apache License 2.0](LICENSE) 下发布的。 240 241 ``` 242 Copyright 2021-2024 TimAndy 243 244 Licensed under the Apache License, Version 2.0 (the "License"); 245 you may not use this file except in compliance with the License. 246 You may obtain a copy of the License at 247 248 https://www.apache.org/licenses/LICENSE-2.0 249 250 Unless required by applicable law or agreed to in writing, software 251 distributed under the License is distributed on an "AS IS" BASIS, 252 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 253 See the License for the specific language governing permissions and 254 limitations under the License. 255 ```