ASP.NET中静态变量锁失效原因:GetYYY()重复初始化问题
锁失效的原因及修复方案
问题根源
你代码里的锁之所以没生效,核心问题出在锁对象被替换了:
- 一开始你用
lock(_data)加锁,但在临界区里执行了_data = new List<YYY>(),直接把_data的引用换成了新的List对象。 - 第一个线程进入锁后替换了
_data,后续线程过来时,lock(_data)锁的是新的List实例,和第一个线程的锁对象完全不是同一个,自然起不到互斥作用。 - 再加上初始
_data是空列表,_data.Count > 0的判断一开始不成立,多个线程会趁机进入各自的锁(因为锁对象不同),重复执行数据库查询和初始化逻辑,这就是日志打印两次不同数值的原因。
修复方案
要解决这个问题,你需要:
- 使用一个固定的、不会被替换的静态锁对象,不能用会被重新赋值的
_data。 - 结合双重检查锁定(Double-Check Locking),减少锁的开销,同时保证线程安全。
- 避免让
_data处于部分初始化的中间状态,防止其他线程读到不完整数据。
修改后的代码示例:
public class XXX { private static List<YYY> _data = new List<YYY>(); // 定义专门的静态锁对象,永远不修改它的引用 private static readonly object _lockObj = new object(); public List<YYY> GetYYY() { // 第一次检查,未初始化再进锁,避免每次调用都加锁 if (_data.Count == 0) { lock (_lockObj) { // 第二次检查,防止多个线程等待锁后重复初始化 if (_data.Count == 0) { try { var query = @" SELECT ..."; using (var dab = GetDataAccessBlock()) { OracleDataReader reader = dab.ExecuteSQLAsReader(query); // 用临时列表先完成数据初始化,避免中间状态暴露 var tempData = new List<YYY>(); while (reader.Read()) { tempData.Add(new YYY { P1 = reader["P1"].ToString(), P2 = ... }); } // 最后一次性赋值给_data,保证原子性 _data = tempData; log.Debug(_data.Count + " values are returned. this should be called only once when app restarts"); } } catch (Exception ex) { log.Error("..."); } } } } return _data; } }
额外说明
- 用临时列表
tempData先完成数据加载,最后再赋值给_data,可以避免其他线程读到还在填充过程中的不完整列表。 - 如果后续不需要修改
_data,可以将其改为IReadOnlyList<YYY>类型,进一步避免意外修改带来的线程安全问题。
内容的提问来源于stack exchange,提问作者shrimp rice
相关产品推荐
相关产品推荐

