ThreadLocal构造时传null直接抛NullReferenceException;工厂函数仅首次访问Value时执行一次;必须显式Dispose防内存泄漏;AsyncLocal才支持async/await上下文流转。
直接抛 NullReferenceException——因为 ThreadLocal 构造函数不接受 null 值作为默认值提供器,且泛型类型 T 为引用类型时,Value 初始读取返回 null 是合法的,但你不能在构造时传 null 当作工厂委托。
正确做法是显式提供初始化逻辑:
ne
w ThreadLocal(() => "default") ,避免首次访问时为 null
int 默认初始化为 0,但若想设为 42,仍需传工厂: new ThreadLocal(() => 42)
不会。工厂函数只在**当前线程首次访问 Value 属性时执行一次**,后续读取直接返回该线程缓存的值。
这正是它和普通局部变量的关键区别:它延迟初始化 + 每线程一份 + 自动隔离。
Regex、StringBuilder、数据库连接上下文)List),那依然不是线程安全的——ThreadLocal 只管“存储位置”隔离,不管里面存的东西本身是否可共享ThreadLocal 的自动行为ThreadLocal 内部持有对每个线程数据的强引用,.NET Framework 中若线程长期存活(如线程池线程),且 ThreadLocal 实例未被释放,其线程局部值不会被 GC 回收。
尤其在 ASP.NET(非 Core)、WinForms 后台线程等场景下容易踩坑。
threadLocal.Dispose()
using 语句块包裹(仅限生命周期明确的场景,如单次任务)byte[] 数兆),泄漏影响更明显根本不在“线程”,而在“执行上下文”:ThreadLocal 绑定物理线程,AsyncLocal 绑定 ExecutionContext,能跨 await 流转。
这意味着:
async/await 方法中,ThreadLocal.Value 在 await 后可能变成另一个线程的值,甚至为初始值(因线程切换)AsyncLocal,而不是 ThreadLocal
ThreadLocal 必须重构成 AsyncLocal