github.com/timandy/routine@v1.1.4-0.20240507073150-e4a3e1fe2ba5/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  ```