ThreadLocal for Golang
背景
由于官方不支持 ThreadLocal,在业务中传参经常需要传递 context,造成参数混乱,开发效率低下,跨方法实现传参变得困难。
需要解决的核心问题:
1、 数据存储,g.labels unsafe.Pointer 字段在业务场景下不会被使用,所以我们可以把数据的指针存储在该字段上。
2、g.labels字段存在一个问题就是在创建子协程的时候指针会被自动复制,而数据不会。所以我们需要在访问该字段时,断该字段的内容是否为当前协程创建的,如果不是则清空。所以这就要求我们需要首先获取 goid,goid 的获取下文会进行说明。
3、垃圾回收,由于数据指针存储在协程结构 g.labels 字段,该字段在协程结束后自动被置为 nil,所以在下次垃圾回收时只要关联的数据没有被额外的引用便会自动回收。
架构

Goid
github 上有很多库已经实现了不同平台的汇编调用 go runtime 库中的 getg()。
然后通过 unsafe 便宜一定的地址空间获取 goid, 该方法与直接调用耗时相当,性能非常高。
在不同的平台上(windows、linux、mac)均能正常获取到。
在极特殊的情况下(目前未发现),如果获取不到会通过 runtime.Stack(false) 方式获取并打印警告日志,此时性能会降低,但能保证不出错。
FastThreadLocal
此设计参考了 netty 中的 FastThreadLocal。
将 threadLocalMap 数据结构由 Map 改为 Slice,在创建 ThreadLocal 实例的时候内部维护一个 id,该 id 为全局自增。
ThreadLocal 实例的 id 字段作为 Slice 的下标,这样在 Get,Set 操作时由原来的操作 Map 变更为通过下标操作 Slice,当 ThreadLocal 实例多的时候性能提升明显。
InheritableThreadLocal 实现
参考 java,在创建协程的时候先复制当前协程的 threadLocalMap,当任务真正的在子协程执行的时候,将复制出来的 threadLocalMap 赋值到当前协程,便可实现跨协程继承数据。
注意,如果是非指针的值数据继承过去的是复制后的值,而指针类型的数据跨协程继承的数据复制的是指针的值。
即在另一个协程对结构内部的字段内容进行修改实际上会影响两个协程。如果要避免这种情况的出现,需要实现 Cloneable 接口。此时复制的时候会将指针指向的内容进行复制一份。
垃圾回收
由于存储数据指针的g.labels字段会被自动清空,所以不需要额外的垃圾回收。
安装
go get github.com/timandy/routine
使用
以下代码简单演示了ThreadLocal的创建、设置、获取、跨协程传播等:
package main
import (
"fmt"
"github.com/timandy/routine"
"time"
)
var threadLocal = routine.NewThreadLocal()
var inheritableThreadLocal = routine.NewInheritableThreadLocal()
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 函数启动一个新的 goroutine。当前主 goroutine 的所有可继承变量都可以自动传递。
routine.Go(func() {
fmt.Println("threadLocal in goroutine by Go:", threadLocal.Get())
fmt.Println("inheritableThreadLocal in goroutine by Go:", inheritableThreadLocal.Get())
})
time.Sleep(time.Second)
}
执行结果为:
threadLocal: hello world
inheritableThreadLocal: Hello world2
threadLocal in goroutine: <nil>
inheritableThreadLocal in goroutine: <nil>
threadLocal in goroutine by Go: <nil>
inheritableThreadLocal in goroutine by Go: Hello world2
支持网格
darwin |
linux |
windows |
freebsd |
||
|---|---|---|---|---|---|
386 |
386 |
||||
amd64 |
amd64 |
||||
armv6 |
armv6 |
||||
armv7 |
armv7 |
||||
arm64 |
arm64 |
||||
ppc64 |
ppc64 |
||||
s390x |
s390x |
||||
darwin |
linux |
windows |
freebsd |
:支持
源码
https://github.com/timandy/routine
ThreadLocal for Golang的更多相关文章
- idou老师教你学Istio 08: 调用链埋点是否真的“零修改”?
本文将结合一个具体例子中的细节详细描述Istio调用链的原理和使用方式.并基于Istio中埋点的原理解释来说明:为了输出一个质量良好的调用链,业务程序需根据自身特点做适当的修改,即并非官方一直在说的完 ...
- Golang源码探索(二) 协程的实现原理
Golang最大的特色可以说是协程(goroutine)了, 协程让本来很复杂的异步编程变得简单, 让程序员不再需要面对回调地狱, 虽然现在引入了协程的语言越来越多, 但go中的协程仍然是实现的是最彻 ...
- Golang源码探索(二) 协程的实现原理(转)
Golang最大的特色可以说是协程(goroutine)了, 协程让本来很复杂的异步编程变得简单, 让程序员不再需要面对回调地狱,虽然现在引入了协程的语言越来越多, 但go中的协程仍然是实现的是最彻底 ...
- 深入Golang调度器之GMP模型
前言 随着服务器硬件迭代升级,配置也越来越高.为充分利用服务器资源,并发编程也变的越来越重要.在开始之前,需要了解一下并发(concurrency)和并行(parallesim)的区别. 并发: 逻 ...
- 面试必问:Golang高阶-Golang协程实现原理
引言 实现并发编程有进程,线程,IO多路复用的方式.(并发和并行我们这里不区分,如果CPU是多核的,可能在多个核同时进行,我们叫并行,如果是单核,需要排队切换,我们叫并发) 进程和线程的区别 进程是计 ...
- 说说Golang goroutine并发那些事儿
摘要:今天我们一起盘点一下Golang并发那些事儿. Golang.Golang.Golang 真的够浪,今天我们一起盘点一下Golang并发那些事儿,准确来说是goroutine,关于多线程并发,咱 ...
- 支持JDK19虚拟线程的web框架,之五(终篇):兴风作浪的ThreadLocal
欢迎访问我的GitHub 这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos <支持JDK19虚拟线程的web框架>系列 ...
- Golang, 以17个简短代码片段,切底弄懂 channel 基础
(原创出处为本博客:http://www.cnblogs.com/linguanh/) 前序: 因为打算自己搞个基于Golang的IM服务器,所以复习了下之前一直没怎么使用的协程.管道等高并发编程知识 ...
- ThreadLocal简单理解
在java开源项目的代码中看到一个类里ThreadLocal的属性: private static ThreadLocal<Boolean> clientMode = new Thread ...
随机推荐
- 关闭StackExchange等平台的privacy收集窗口
技术背景 当我们打开一个StackExchange页面的时候,经常会出现一个很大的privacy收集窗口,而且不管怎么点都关闭不了,比如像下图这样: 如果屏幕足够大,影响可能也不是很大,但是关键是对于 ...
- 将Hexo搭建到自己的服务器上
http://xybin.top/posts/9373.html 第一部分:服务器端的操作 1.安装git 和nginx yum install -y nginx git 2.添加一个git用户 #添 ...
- 9.Linux之iptables防火墙
Linux之iptables防火墙 目录 Linux之iptables防火墙 iptables防火墙概述 netfilter和iptables之间的关系 netfilter iptables ipta ...
- 七、服务器硬件及RAID配置实战
一.RAID磁盘阵列介绍 磁盘阵列的全名(Redundant Arrays of Inexpensive Disk,RAID),中文简称是独立冗余磁盘阵列.冗余(如果磁盘出现故障,可以保证数据不丢) ...
- python+anaconda+pycharm的使用
研一开学的时候开始接触了这些,但是对于其各种功能感到十分混乱,现在通过这篇博文将其功能详细的写出来. 1.python解释器 首先要了解python解释器,我们俗称的下载python也就是下载pyth ...
- 一篇文章讲清楚MySQL的聚簇/联合/覆盖索引、回表、索引下推
迎面走来了你的面试官,身穿格子衫,挺着啤酒肚,发际线严重后移的中年男子. 手拿泡着枸杞的保温杯,胳膊夹着MacBook,MacBook上还贴着公司标语:"加班使我快乐". 面试官: ...
- XSS攻击(笔记)
XSS攻击 XSS概述 XSS即跨站脚本攻击,(Cross-Site Scripting, CSS),但是为了与层叠样式表(Cascading Style Sheets, CSS)缩写区分开来,所以命 ...
- 关于android里activity之间利用button组件使用intent跳转页面
在需要跳转的activity 中 添加 Button button = findViewById(R.id.login);button.setOnClickListener(new View.OnCl ...
- kubernetes集群简单实例搭建
systemctl stop firewalld && systemctl disable firewalldvim /etc/selinux/configSELINUX=disabl ...
- ArrayList和LinkedList内部是怎么实现的?他们之间的区别和优缺点?
ArrayList 内部使用了数组形式进行了存储,利用数组的下标进行元素的访问,因此对元素的随机访问速度非常快.因为是数组,所以ArrayList在初始化的时候, 有初始大小10,插入新元素的时候,会 ...