我的C#带元素过期功能的TokenCache实现是否线程安全?
你的TokenCache实现问题分析
1. 当前实现的直接问题
- 重复添加同一key会导致提前过期:每次调用
Add同一key时,都会生成一个新的过期删除任务。比如第一次添加key后,过了一半时间再次添加该key,旧的过期任务仍会在原定时时间执行Remove,把刚添加的新值删掉,导致新值没到设定时间就被移除。 - 线程池资源浪费:用
Task.Run(() => Thread.Sleep(expirationTime))会占用线程池线程,大量添加元素时会消耗线程池资源,影响其他依赖线程池的操作性能。 - 异常被静默忽略:过期任务的
ContinueWith没有处理异常,一旦Remove过程中出现异常(比如信号量异常),异常会被吞掉,难以排查问题。 - 过期时间不精确:
Thread.Sleep的实际等待时间受线程调度影响,会比设定的expirationTime偏长,导致过期时间不准。
2. 是否属于开箱即用的线程安全实现?
答案是不能完全算,理由如下:
- 过期任务的并发冲突:同一key的多个过期任务会重复执行
Remove,如果两次Remove之间有其他线程重新添加了该key,后续的Remove会错误删除刚添加的有效数据。 - 缺少资源释放机制:
SemaphoreSlim实现了IDisposable接口,但你的类没有实现IDisposable,长时间运行会导致信号量资源泄漏。 - 未保护存储对象的线程安全:
GetValueOrDefault返回的TValue如果是可变类型,外部线程可以直接修改该对象,而你的类只保护了字典的结构访问,没有对存储对象本身做线程安全防护。
优化建议(可选)
- 给每个缓存项存储过期时间戳,用Timer定期扫描字典移除过期项,避免大量线程池任务。
- 重复添加key时,更新对应项的过期时间戳,而不是生成新任务。
- 实现
IDisposable接口,释放SemaphoreSlim和Timer等资源。 - 如果需要保护
TValue的线程安全,可考虑存储不可变对象,或在返回时提供对象副本(若适用)。
内容的提问来源于stack exchange,提问作者Resnick
相关产品推荐
相关产品推荐

