线程安全加载静态集合

时间:2013-11-29 13:21:31

标签: c# asp.net thread-safety

我有一些静态的Dictionary对象,它为我保存了一些常量列表,所以每次我的网站加载时我都不必从数据库加载它们(例如:国家列表,类别列表)。

所以我有一个静态函数来检查实例是否为null,如果是查询数据库,则实例化静态变量,并用数据填充它。

由于它是一个网站,可能会有一个案例,当对象为空时,不止一个人试图同时访问该信息,所有做的人将同时调用该进程(这是实际上没有必要,导致对数据库的不必要的查询,并可能导致列表中的重复对象。

我知道有一种方法可以使这种加载线程安全(只是不确定如何) - 有人能指出我正确的方向吗?我应该使用锁?

由于

更新II:

这就是我写的(这是一个很好的线程安全代码吗?)

private static Lazy<List<ICountry>> _countries  = new Lazy<List<ICountry>>(loadCountries);

private static List<ICountry> loadCountries()
{
        List<ICountry> result = new List<ICountry>();

        DataTable dtCountries = SqlHelper.ExecuteDataTable("stp_Data_Countries_Get");
        foreach (DataRow dr in dtCountries.Rows)
        {
            result.Add(new Country
                {
                    ID = Convert.ToInt32(dr["CountryId"]),
                    Name = dr["Name"].ToString()
                });
        }

        return result;
}

public static List<ICountry> GetAllCountries()
{
    return _countries.Value;
}

2 个答案:

答案 0 :(得分:0)

如果您使用的是.NET 4.0,则可以使用内置的Lazy泛型类。

private static Lazy<YourObject> data = new Lazy<YourObject>(YourInitializationFunction);
public static YourObject Data { get { return data.Value; } }

请注意,您必须将静态构造函数添加到您定义它的类中,否则它不是完全线程安全的。

如果您不使用.NET 4.0+,则可以编写自己的代码。基本模式看起来像这样:

private static YourObject data;
private static object syncObject = new object();

public static YourObject Data
{
  get
    {
        if (data == null)
        {
            lock (syncObject)
            {
                if (data != null)
                    return data;

                var obj = new YourObject();

                return (YourObject)Interlocked.Exchange(ref data, obj);
            }
        }

        return data;
    }
}

答案 1 :(得分:0)

您可以使用Lazy以惰性和线程安全的方式加载资源:

Lazy<List<string>> countries = 
    new Lazy<List<string>>(()=> /* get your countries from db */);

更新

public static class HelperTables
{
   private static Lazy<List<ICountry>> _countries;

   static HelperTables //Static constructor
   {
       //Instantiating the lazy object in the static constructor will prevent race conditions
      _countries = new Lazy<List<ICountry>>(() =>
      {
        List<ICountry> result = new List<ICountry>();

        DataTable dtCountries = SqlHelper.ExecuteDataTable("stp_Data_Countries_Get");
        foreach (DataRow dr in dtCountries.Rows)
        {
            result.Add(new Country
            {
                ID = Convert.ToInt32(dr["CountryId"]),
                Name = dr["Name"].ToString()
            });
        }

        return result;
      });
   }

   public static List<ICountry> GetAllCountries()
   {
      return _countries.Value;
   }
}