0

0

c# ConditionalWeakTable 的作用和线程安全

月夜之吻

月夜之吻

发布时间:2026-01-19 13:58:02

|

697人浏览过

|

来源于php中文网

原创

ConditionalWeakTable 是 .NET 中用于为对象附加生命周期绑定元数据的线程安全集合,仅弱引用键、强引用值,键回收后条目自动移除;不支持枚举或 LINQ,适用于动态装饰对象,但需警惕值反向持有键导致的循环引用内存泄漏。

c# conditionalweaktable 的作用和线程安全

ConditionalWeakTable 是什么,为什么不用 Dictionary

ConditionalWeakTable 是 .NET 提供的一个特殊集合类型,核心用途是「给任意对象附加生命周期绑定的元数据」。它不阻止键对象被 GC 回收,一旦键被回收,对应条目自动消失——这点和 Dictionary 有本质区别Dictionary 会强引用键,导致本该回收的对象滞留。

典型场景包括:为第三方类型(比如 FileStream 或用户自定义类)动态挂载上下文、诊断信息、AOP 行为钩子等,且不干预其生命周期。

  • 键(K)必须是引用类型,且内部用弱引用来跟踪
  • 值(V)是强引用,但键被回收后,整个条目从表中移除(即使值还活着)
  • 不支持枚举(foreach)、Count 属性或 LINQ 查询——它不是通用容器,而是“附着式存储”

ConditionalWeakTable 的线程安全性

ConditionalWeakTable 的所有公开方法(AddGetValueTryGetValueRemove)都是线程安全的。内部使用细粒度锁 + 无锁路径混合实现,.NET Core 2.1+ 还进一步优化了读多写少场景的性能。

但要注意:线程安全仅保证单个方法调用原子性,不保证复合操作的原子性。例如下面这段代码就有竞态风险:

var table = new ConditionalWeakTable();
if (!table.TryGetValue(obj, out _))
{
    table.Add(obj, ComputeValue()); // 可能被多个线程同时执行
}

正确做法是用 GetValue,它自带“首次调用初始化”语义:

华友协同办公自动化OA系统
华友协同办公自动化OA系统

华友协同办公管理系统(华友OA),基于微软最新的.net 2.0平台和SQL Server数据库,集成强大的Ajax技术,采用多层分布式架构,实现统一办公平台,功能强大、价格便宜,是适用于企事业单位的通用型网络协同办公系统。 系统秉承协同办公的思想,集成即时通讯、日记管理、通知管理、邮件管理、新闻、考勤管理、短信管理、个人文件柜、日程安排、工作计划、工作日清、通讯录、公文流转、论坛、在线调查、

下载
var value = table.GetValue(obj, _ => ComputeValue());
  • GetValue 内部确保:对同一键,最多只调用一次工厂函数,其余线程阻塞等待结果
  • 工厂函数(Func)内不能依赖外部可变状态,否则可能引发不可预期行为
  • 如果工厂函数抛异常,该异常会被缓存并重抛;后续对该键的 GetValue 调用仍会抛出同一异常

常见误用和内存泄漏隐患

最隐蔽的问题不是线程安全,而是误以为值的生命周期也受弱引用保护。实际上:ConditionalWeakTable 只弱引用键,值是强引用。如果值反过来持有键的引用(比如闭包捕获、事件订阅、内部字段赋值),就会形成循环引用,导致键无法被 GC —— 弱引用失效,内存泄漏发生。

  • 避免在值类型中保存对键的强引用,尤其注意 lambda、匿名类、委托实例
  • 若值需监听键的事件,务必在键释放前手动解绑(但键释放不可控,推荐改用 WeakEventManager 或弱事件模式)
  • 不要把它当缓存用:没有过期策略、不支持容量控制、不触发 GC 友好清理
  • 调试时看不到条目?因为 Visual Studio 的调试器会临时强引用对象,干扰弱引用观察 —— 需用内存快照(dotMemory / VS Diagnostic Tools)验证实际存活情况

替代方案对比:WeakReference vs ConditionalWeakTable

如果你只是想“弱持有某个对象”,用 WeakReference 更轻量;但如果你想“给任意已有对象加字段”,ConditionalWeakTable 是唯一选择。

  • WeakReference:你主动创建、持有、查询,适合缓存单个对象引用
  • ConditionalWeakTable:你把对象当键“贴标签”,框架帮你管理弱关联,适合装饰/扩展未知对象
  • 两者都不解决“值引用键”的循环问题,这始终要靠设计规避

真正容易被忽略的是:它的存在本身就意味着你在做“运行时对象增强”,这种模式会让代码路径更难追踪、GC 行为更难预测——上线前务必用真实负载压测内存驻留曲线。

相关专题

更多
counta和count的区别
counta和count的区别

Count函数用于计算指定范围内数字的个数,而CountA函数用于计算指定范围内非空单元格的个数。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

197

2023.11.20

php中foreach用法
php中foreach用法

本专题整合了php中foreach用法的相关介绍,阅读专题下面的文章了解更多详细教程。

42

2025.12.04

lambda表达式
lambda表达式

Lambda表达式是一种匿名函数的简洁表示方式,它可以在需要函数作为参数的地方使用,并提供了一种更简洁、更灵活的编码方式,其语法为“lambda 参数列表: 表达式”,参数列表是函数的参数,可以包含一个或多个参数,用逗号分隔,表达式是函数的执行体,用于定义函数的具体操作。本专题为大家提供lambda表达式相关的文章、下载、课程内容,供大家免费下载体验。

204

2023.09.15

python lambda函数
python lambda函数

本专题整合了python lambda函数用法详解,阅读专题下面的文章了解更多详细内容。

190

2025.11.08

Python lambda详解
Python lambda详解

本专题整合了Python lambda函数相关教程,阅读下面的文章了解更多详细内容。

49

2026.01.05

线程和进程的区别
线程和进程的区别

线程和进程的区别:线程是进程的一部分,用于实现并发和并行操作,而线程共享进程的资源,通信更方便快捷,切换开销较小。本专题为大家提供线程和进程区别相关的各种文章、以及下载和课程。

481

2023.08.10

go语言闭包相关教程大全
go语言闭包相关教程大全

本专题整合了go语言闭包相关数据,阅读专题下面的文章了解更多相关内容。

135

2025.07.29

PHP WebSocket 实时通信开发
PHP WebSocket 实时通信开发

本专题系统讲解 PHP 在实时通信与长连接场景中的应用实践,涵盖 WebSocket 协议原理、服务端连接管理、消息推送机制、心跳检测、断线重连以及与前端的实时交互实现。通过聊天系统、实时通知等案例,帮助开发者掌握 使用 PHP 构建实时通信与推送服务的完整开发流程,适用于即时消息与高互动性应用场景。

11

2026.01.19

微信聊天记录删除恢复导出教程汇总
微信聊天记录删除恢复导出教程汇总

本专题整合了微信聊天记录相关教程大全,阅读专题下面的文章了解更多详细内容。

73

2026.01.18

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
极客学院Java8新特性视频教程
极客学院Java8新特性视频教程

共17课时 | 3.8万人学习

PHP新手语法线上课程教学
PHP新手语法线上课程教学

共13课时 | 0.9万人学习

光速学会docker容器
光速学会docker容器

共33课时 | 1.9万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号