Skip to content

Latest commit

 

History

History
255 lines (178 loc) · 11.5 KB

README_zh.md

File metadata and controls

255 lines (178 loc) · 11.5 KB

routine

Build Status Codecov Go Report Card Documentation Release License

English Version

routine封装并提供了一些易用、无竞争、高性能的goroutine上下文访问接口,它可以帮助你更优雅地访问协程上下文信息。

介绍

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

如果你使用过其他语言如C++Java等,那么你一定很熟悉ThreadLocal,而在开始使用Golang之后,你一定会为缺少类似ThreadLocal的便捷功能而深感困惑与苦恼。

当然你可以选择使用Context,让它携带着全部上下文信息,在所有函数的第一个输入参数中出现,然后在你的系统中到处穿梭。

routine的核心目标就是开辟另一条路:将goroutine local storage引入Golang世界。

使用演示

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

安装

go get github.com/timandy/routine

使用goid

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

package main

import (
	"fmt"
	"time"

	"github.com/timandy/routine"
)

func main() {
	goid := routine.Goid()
	fmt.Printf("cur goid: %v\n", goid)
	go func() {
		goid := routine.Goid()
		fmt.Printf("sub goid: %v\n", goid)
	}()

	// 等待子协程执行完。
	time.Sleep(time.Second)
}

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

cur goid: 1
sub goid: 6

使用ThreadLocal

以下代码简单演示了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("threadLocal in goroutine:", threadLocal.Get())
		fmt.Println("inheritableThreadLocal in goroutine:", inheritableThreadLocal.Get())
	}()

	// 但是,可以通过 Go/GoWait/GoWaitResult 函数启动一个新的子协程,当前协程的所有可继承变量都可以自动传递。
	routine.Go(func() {
		fmt.Println("threadLocal in goroutine by Go:", threadLocal.Get())
		fmt.Println("inheritableThreadLocal in goroutine by Go:", inheritableThreadLocal.Get())
	})

	// 也可以通过 WrapTask/WrapWaitTask/WrapWaitResultTask 函数创建一个任务,当前协程的所有可继承变量都可以被自动捕获。
	task := routine.WrapTask(func() {
		fmt.Println("threadLocal in task by WrapTask:", threadLocal.Get())
		fmt.Println("inheritableThreadLocal in task by WrapTask:", inheritableThreadLocal.Get())
	})
	go task.Run()

	// 等待子协程执行完。
	time.Sleep(time.Second)
}

执行结果为:

threadLocal: hello world
inheritableThreadLocal: Hello world2
threadLocal in goroutine:
inheritableThreadLocal in goroutine:
threadLocal in goroutine by Go:
inheritableThreadLocal in goroutine by Go: Hello world2
threadLocal in task by WrapTask:
inheritableThreadLocal in task by WrapTask: Hello world2

API文档

此章节详细介绍了routine库封装的全部接口,以及它们的核心功能、实现方式等。

Goid() int64

获取当前goroutinegoid

386amd64armv6armv7arm64loong64mipsmipslemips64mips64leppc64ppc64leriscv64s390xwasm架构下通过汇编代码直接获取,此操作性能极高,耗时通常只相当于rand.Int()的五分之一。

NewThreadLocal[T any]() ThreadLocal[T]

创建一个新的ThreadLocal[T]实例,其存储的初始值为类型T的默认值。

NewThreadLocalWithInitial[T any](supplier Supplier[T]) ThreadLocal[T]

创建一个新的ThreadLocal[T]实例,其存储的初始值为方法supplier()的返回值。

NewInheritableThreadLocal[T any]() ThreadLocal[T]

创建一个新的ThreadLocal[T]实例,其存储的初始值为类型T的默认值。 当通过Go()GoWait()GoWaitResult()启动新协程时,当前协程的值会被复制到新协程。 当通过WrapTask()WrapWaitTask()WrapWaitResultTask()创建任务时,当前协程的值会被捕获。

NewInheritableThreadLocalWithInitial[T any](supplier Supplier[T]) ThreadLocal[T]

创建一个新的ThreadLocal[T]实例,其存储的初始值为方法supplier()的返回值。 当通过Go()GoWait()GoWaitResult()启动新协程时,当前协程的值会被复制到新协程。 当通过WrapTask()WrapWaitTask()WrapWaitResultTask()创建任务时,当前协程的值会被捕获。

WrapTask(fun Runnable) FutureTask[any]

创建一个新任务,并捕获当前协程的inheritableThreadLocals。 此函数返回一个FutureTask实例,但返回的任务不会自动运行。 你可以通过FutureTask.Run()方法在子协程或协程池中运行它,通过FutureTask.Get()FutureTask.GetWithTimeout()方法等待任务执行完毕。 任务执行时的任何panic都会被捕获并打印错误堆栈,在调用FutureTask.Get()FutureTask.GetWithTimeout()方法时panic会被再次抛出。

WrapWaitTask(fun CancelRunnable) FutureTask[any]

创建一个新任务,并捕获当前协程的inheritableThreadLocals。 此函数返回一个FutureTask实例,但返回的任务不会自动运行。 你可以通过FutureTask.Run()方法在子协程或协程池中运行它,通过FutureTask.Get()FutureTask.GetWithTimeout()方法等待任务执行完毕。 任务执行时的任何panic都会被捕获,在调用FutureTask.Get()FutureTask.GetWithTimeout()方法时panic会被再次抛出。

WrapWaitResultTask[TResult any](fun CancelCallable[TResult]) FutureTask[TResult]

创建一个新任务,并捕获当前协程的inheritableThreadLocals。 此函数返回一个FutureTask实例,但返回的任务不会自动运行。 你可以通过FutureTask.Run()方法在子协程或协程池中运行它,通过FutureTask.Get()FutureTask.GetWithTimeout()方法等待任务执行完毕并获取结果。 任务执行时的任何panic都会被捕获,在调用FutureTask.Get()FutureTask.GetWithTimeout()方法时panic会被再次抛出。

Go(fun Runnable)

启动一个新的协程,同时自动将当前协程的全部上下文inheritableThreadLocals数据复制至新协程。 子协程执行时的任何panic都会被捕获并自动打印堆栈。

GoWait(fun CancelRunnable) FutureTask[any]

启动一个新的协程,同时自动将当前协程的全部上下文inheritableThreadLocals数据复制至新协程。 可以通过返回值的FutureTask.Get()FutureTask.GetWithTimeout()方法等待子协程执行完毕。 子协程执行时的任何panic都会被捕获并在调用FutureTask.Get()FutureTask.GetWithTimeout()时再次抛出。

GoWaitResult[TResult any](fun CancelCallable[TResult]) FutureTask[TResult]

启动一个新的协程,同时自动将当前协程的全部上下文inheritableThreadLocals数据复制至新协程。 可以通过返回值的FutureTask.Get()FutureTask.GetWithTimeout()方法等待子协程执行完毕并获取返回值。 子协程执行时的任何panic都会被捕获并在调用FutureTask.Get()FutureTask.GetWithTimeout()时再次抛出。

更多API文档

垃圾回收

routine为每个协程分配了一个thread结构,它存储了协程相关的上下文变量信息。

指向该结构的指针存储在协程结构的g.labels字段上。

当协程执行完毕退出时,g.labels将被设置为nil,不再引用thread结构。

thread结构将在下次GC时被回收。

如果thread中存储的数据也没有额外被引用,这些数据将被一并回收。

支持网格

darwin linux windows freebsd js
386 386
amd64 amd64
armv6 armv6
armv7 armv7
arm64 arm64
loong64 loong64
mips mips
mipsle mipsle
mips64 mips64
mips64le mips64le
ppc64 ppc64
ppc64le ppc64le
riscv64 riscv64
s390x s390x
wasm wasm
darwin linux windows freebsd js

✅:支持

鸣谢

感谢所有贡献者的贡献!

许可证

routine是在 Apache License 2.0 下发布的。

Copyright 2021-2024 TimAndy

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    https://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.