ThreadLocal

字段成员:

private Func<T>? _valueFactory;

一个获取默认值的委托 不同线程共享此成员。

[ThreadStatic]
private static LinkedSlotVolatile[]? ts_slotArray;

ThreadStatic特性,这不就是我们熟悉的ThreadStaticAttribute吗,

所以ThreadLocal 就是一个ThreadStatic的封装类,简化了tls操作

[ThreadStatic]
private static FinalizationHelper? ts_finalizationHelper;

见名思义,用于释放的帮助类

private int _idComplement;

Slot ID of this ThreadLocal<instance.

这个ThreadLocal<>实例的槽ID。

We store a bitwise complement of the ID (that is ~ID), which allows us to distinguish

我们存储ID的位补码(即~ID),这使我们能够区分

between the case when ID is 0 and an incompletely initialized object, either due to a thread abort in the constructor, or

在ID为0的情况和未完全初始化的对象之间,原因可能是构造函数中的线程中止,也可能是

possibly due to a memory model issue in user code.

可能是由于用户代码中的内存模型问题。

用于区分是否初始化。

private volatile bool _initialized;

表示对象是否完全初始化..

private volatile bool _initialized;

是否初始化-构造函数

private static readonly IdManager s_idManager = new IdManager();

IdManager assigns and reuses slot IDs.

IdManager分配和重用插槽id。

Additionally, the object is also used as a global lock.

此外,该对象还用作全局锁。

private LinkedSlot? _linkedSlot = new LinkedSlot(null);

伪头节点

private bool _trackAllValues;

是否支持Values属性

方法

private void Initialize(Func<T>? valueFactory, bool trackAllValues)
{
_valueFactory = valueFactory;
_trackAllValues = trackAllValues; // Assign the ID and mark the instance as initialized. To avoid leaking IDs, we assign the ID and set _initialized
// in a finally block, to avoid a thread abort in between the two statements.
try { }
finally
{
_idComplement = ~s_idManager.GetId(); // As the last step, mark the instance as fully initialized. (Otherwise, if _initialized=false, we know that an exception
// occurred in the constructor.)
_initialized = true;
}
}

初始化方法,所有构造通过此方法初始化。

查看IdManager的GetId方法:

internal int GetId()
{
List<bool> freeIds = this.m_freeIds;
lock (freeIds)
{
int nextIdToTry = this.m_nextIdToTry;
while (nextIdToTry < this.m_freeIds.Count)
{
if (this.m_freeIds[nextIdToTry])
{
break;
}
nextIdToTry++;
}
if (nextIdToTry == this.m_freeIds.Count)
{
this.m_freeIds.Add(false);
}
else
{
this.m_freeIds[nextIdToTry] = false;
}
this.m_nextIdToTry = nextIdToTry + 1;
return nextIdToTry;
}
}

具体就不说明了,类似于数据库中的自增标识

注:由于ThreadLocal为泛型类,仅当构造同类型的ThreadLocal才会触发自增

这里我们也可以知道为何需要一个LinkedSlotVolatile数组

当线程中存在多个ThreadLocal即存在多个泛型类型相同的ThreadLocal,就需要使用数组进行存储,而_idComplement就是充当一个数组下标的功能

public T Value
{
get
{
LinkedSlotVolatile[]? slotArray = ts_slotArray;
LinkedSlot? slot;
int id = ~_idComplement; //
// Attempt to get the value using the fast path
//
if (slotArray != null // Has the slot array been initialized?
&& id >= 0 // Is the ID non-negative (i.e., instance is not disposed)?
&& id < slotArray.Length // Is the table large enough?
&& (slot = slotArray[id].Value) != null // Has a LinkedSlot object has been allocated for this ID?
&& _initialized // Has the instance *still* not been disposed (important for a race condition with Dispose)?
)
{
// We verified that the instance has not been disposed *after* we got a reference to the slot.
// This guarantees that we have a reference to the right slot.
//
// Volatile read of the LinkedSlotVolatile.Value property ensures that the m_initialized read
// will not be reordered before the read of slotArray[id].
return slot._value;
} return GetValueSlow();
}
set
{
LinkedSlotVolatile[]? slotArray = ts_slotArray;
LinkedSlot? slot;
int id = ~_idComplement; // Attempt to set the value using the fast path
if (slotArray != null // Has the slot array been initialized?
&& id >= 0 // Is the ID non-negative (i.e., instance is not disposed)?
&& id < slotArray.Length // Is the table large enough?
&& (slot = slotArray[id].Value) != null // Has a LinkedSlot object has been allocated for this ID?
&& _initialized // Has the instance *still* not been disposed (important for a race condition with Dispose)?
)
{
// We verified that the instance has not been disposed *after* we got a reference to the slot.
// This guarantees that we have a reference to the right slot.
//
// Volatile read of the LinkedSlotVolatile.Value property ensures that the m_initialized read
// will not be reordered before the read of slotArray[id].
slot._value = value;
}
else
{
SetValueSlow(value, slotArray);
}
}
}

如果slotArray中有值就操作slotArray ,否则就

  • 写-更新slotArray

  • 读-从_valueFactory 取值

到这里就差不多了,over~


https://github.com/dotnet/coreclr/blob/9773db1e7b1acb3ec75c9cc0e36bd62dcbacd6d5/src/System.Private.CoreLib/shared/System/Threading/ThreadLocal.cs

C# ThreadLocal源码追踪的更多相关文章

  1. 并发编程(四)—— ThreadLocal源码分析及内存泄露预防

    今天我们一起探讨下ThreadLocal的实现原理和源码分析.首先,本文先谈一下对ThreadLocal的理解,然后根据ThreadLocal类的源码分析了其实现原理和使用需要注意的地方,最后给出了两 ...

  2. Java多线程9:ThreadLocal源码剖析

    ThreadLocal源码剖析 ThreadLocal其实比较简单,因为类里就三个public方法:set(T value).get().remove().先剖析源码清楚地知道ThreadLocal是 ...

  3. Java多线程学习之ThreadLocal源码分析

    0.概述 ThreadLocal,即线程本地变量,是一个以ThreadLocal对象为键.任意对象为值的存储结构.它可以将变量绑定到特定的线程上,使每个线程都拥有改变量的一个拷贝,各线程相同变量间互不 ...

  4. Java并发编程之ThreadLocal源码分析

    ## 1 一句话概括ThreadLocal<font face="微软雅黑" size=4>  什么是ThreadLocal?顾名思义:线程本地变量,它为每个使用该对象 ...

  5. ThreadLocal源码解读

    1. 背景 ThreadLocal源码解读,网上面早已经泛滥了,大多比较浅,甚至有的连基本原理都说的很有问题,包括百度搜索出来的第一篇高访问量博文,说ThreadLocal内部有个map,键为线程对象 ...

  6. 源码追踪,解决Could not locate executable null\bin\winutils.exe in the Hadoop binaries.问题

    在windows系统本地运行spark的wordcount程序,会出现一个异常,但不影响现有程序运行. >>提君博客原创  http://www.cnblogs.com/tijun/  & ...

  7. ThreadLocal详解,ThreadLocal源码分析,ThreadLocal图解

    本文脉路: 概念阐释 ---->  原理图解  ------> 源码分析 ------>  思路整理  ----> 其他补充. 一.概念阐述. ThreadLocal 是一个为 ...

  8. Saiku登录源码追踪.(十三)

    Saiku登录源码追踪呀~ >>首先我们需要debug跟踪saiku登录执行的源码信息 saiku源码的debug方式上一篇博客已有说明,这里简单介绍一下 在saiku启动脚本中添加如下命 ...

  9. 【JAVA】ThreadLocal源码分析

    ThreadLocal内部是用一张哈希表来存储: static class ThreadLocalMap { static class Entry extends WeakReference<T ...

随机推荐

  1. flex布局制作自适应网页

    网页布局是css的一个重点应用.传统的布局都是依赖display.position.float属性来实现的,但是特殊布局就不易实现,如垂直居中. 01 flex布局是什么?‍ Flex 是 Flexi ...

  2. Day2基本数据类型 字节 和类型转换

    Java基础语法 注释 1.单行注释:// 加内容 2.多行注释:/* 多行注释 */ 3.文档注释: /** * * */ 有趣的注释 标识符 关键字 基本数据类型 八大基本数据类型 //整数​in ...

  3. jquery性能优化建议-上篇

    一.注意定义jQuery变量的时候添加var关键字这个不仅仅是jQuery,所有javascript开发过程中,都需要注意,请一定不要定义成如下:$loading = $('#loading'); / ...

  4. Unittest方法 -- 测试分离

    一.下面是it.py 脚本,把浏览器前置和后置条件分离了"""套件公用测试类可进行分离"""import unittestfrom sele ...

  5. spring-2-AOP

    AOP(面向切面编程) 面向切面编程, 即利用AOP可以对业务逻辑的各个部分进行隔离, 从而使得业务逻辑各个部分之间的耦合度降低, 提高程序的可重用性, 同时提高了开发的效率. 不通过修改源代码,通过 ...

  6. Webstorm 快速补全

    el-row>el-col*3>[:span='7'] 按Tab <el-row> <el-col> <div :span="">& ...

  7. angular5采坑之旅

    开始尝试angular5,在此记录下踩过的坑以备查询 1.按照element-angular的文档引入后报错 is not part of the compilation output.解决方法--在 ...

  8. Vue 脚手架学习

    首先就是安装脚手架 npm install @vue/cil -g 全局安装 在这里我遇到一个问题:安装不了脚手架,报错显示: 通过苦逼的查找原因就是 以前使用的taobao镜像 导致的,删除镜像换成 ...

  9. 05.表达式目录树Expression

    参考文章 https://www.cnblogs.com/xyh9039/p/12748983.html 1. 基本了解 1.1 Lambda表达式 演变过程 using System; namesp ...

  10. 跟我一起写 Makefile(十一)

    make 的运行 ------ 一般来说,最简单的就是直接在命令行下输入make命令,make命令会找当前目录的makefile来执行,一切都是自动的.但也有时你也许只想让make重编译某些文件,而不 ...