You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ASP.NET中静态变量锁失效原因:GetYYY()重复初始化问题

锁失效的原因及修复方案

问题根源

你代码里的锁之所以没生效,核心问题出在锁对象被替换了:

  • 一开始你用lock(_data)加锁,但在临界区里执行了_data = new List<YYY>(),直接把_data的引用换成了新的List对象。
  • 第一个线程进入锁后替换了_data,后续线程过来时,lock(_data)锁的是新的List实例,和第一个线程的锁对象完全不是同一个,自然起不到互斥作用。
  • 再加上初始_data是空列表,_data.Count > 0的判断一开始不成立,多个线程会趁机进入各自的锁(因为锁对象不同),重复执行数据库查询和初始化逻辑,这就是日志打印两次不同数值的原因。

修复方案

要解决这个问题,你需要:

  1. 使用一个固定的、不会被替换的静态锁对象,不能用会被重新赋值的_data。
  2. 结合双重检查锁定(Double-Check Locking),减少锁的开销,同时保证线程安全。
  3. 避免让_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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 23:42:38