用C#中的数据库值填充ConcurrentDictionary时的锁定注意事项

本文关键字:ConcurrentDictionary 锁定 注意事项 填充 数据库 | 更新日期: 2023-09-27 18:29:01

在此处填充ConcurrentDictionary时,是否需要使用锁(lockObj){}块?作为一个小背景,这将在MVC应用程序中使用,尽管我怀疑场景问题与任何多线程应用程序都相关。

在搜索stackoverflow时,我没有找到这个确切的场景。当第一次从GetOptionById值中请求值时,它可以由两个独立的线程调用。

1) 将List对象值设置为私有静态,以期在ConcurrentDictionary填充之前不多次调用数据库,这是否被认为是更好的做法?

2) 这(上面的#1)是必要的吗?还是ConcurrentDictionary足够聪明,可以自己解决这个问题?提前感谢您的任何意见。

public class MyOptions
{
    static string GetOptionById(int id)
    {
        if (options == null || options.Count <= 0)
            FillOptionList();
        return options[id];
    }
    static void FillOptionList()
    {
        List<MyBusinessObject> objects = DataAccessLayer.GetList();
        foreach (MyBusinessObject obj in objects)
            options.TryAdd(obj.Id, obj.Name);
    }
    private static ConcurrentDictionary<int, string> options = new ConcurrentDictionary<int, string>();
}

编辑:谢谢大家的意见,这会是一个更安全的方法吗?

    public static string OptionById(int id)
    {
        if (!options.ContainsKey(id))
        {
            //perhaps this is a new option and we need to reload the list
            FillOptionsOrReturn(true /*force the fill*/);
            return (!options.ContainsKey(id)) ? "Option not found" : options[id];
        }
        else
            return options[id];
    }
    private static void FillOptionsOrReturn(bool forceFill = false)
    {
        List<MyBusinessClass> objectsFromDb = null;
        lock (lockObj)
        {
            if (forceFill || options == null || options.Keys.Count <= 0)
                reasons = DataAccessLayer.GetList();
        }
        if (objectsFromDb != null)
        {
            foreach (MyBusinessClass myObj in objectsFromDb)
                options.TryAdd(myObj.id, myObj.name);
        }
    }
    private static ConcurrentDictionary<int, string> options = new ConcurrentDictionary<int, string>();
    private static object lockObj = new object();

用C#中的数据库值填充ConcurrentDictionary时的锁定注意事项

你所拥有的绝对不安全。考虑:

线程X和Y大致同时调用CCD_ 1。X发现它需要填充字典,并开始这样做。第一个结果返回,并添加到字典中。

然后Y发现有一个条目,并假设字典是完整的,因此它获取它感兴趣的选项,该选项可能不是已经加载的选项。

这看起来是使用Lazy<T>的一个很好的候选者。。。您可以在那里选择适当的选项,这样一次只能有一个线程填充字典——第二个线程会等到第一个线程完成后再继续。这样,"填充字典"就有效地变成了原子。

如果您在第一次加载后从不需要更新字典,那么您甚至可以只使用Lazy<Dictionary<string, string>>——只要没有编写器,就可以安全地拥有多个阅读器。我相信Lazy<T>将适当地处理内存障碍。

以下问题可能会在您的代码中按原样发生。如果它们是可接受的,那么您就可以了,如果它们不是,那么您将需要使用锁。

  1. 多个线程可以实现options是null并重新创建词典。这将导致多次填充
  2. 线程可以从中读取词典,而其中一些(但不是全部)项目添加

当我填充我的这里是ConcurrentDictionary?

不,此数据结构上的方法已经是线程安全的。

1) 使List对象具有值是否被认为是更好的做法一个你锁在周围希望不调用是否在填充ConcurrentDictionary之前多次使用数据库?

也许吧,特别是如果GetList本身还不是线程安全的。除非你的建议行不通。List<MyBusinessObject>实例是从GetList返回的,因此您无法锁定尚未存在的内容。相反,您将创建一个单独的对象,仅用于锁定目的。

2) 这(上面的#1)是必要的吗?还是ConcurrentDictionary聪明到可以自己解决这个问题?

不,没有任何魔法会以某种方式导致GetList被串行执行。

顺便说一下,你的GetOptionById0有比赛条件。多个线程可以同时进入if块。您的代码可能会多次尝试初始化字典。

ConcurrentDictionary仅为访问列表元素提供线程安全性。另一方面,您的FillOptionList方法可以从不同的线程调用多次,所有这些都可以愉快地轮流将值插入集合中。

为了避免这种情况,需要锁定的不是集合本身,而是GetOptionById内部的条件检查。

相关文章:
  • 没有找到相关文章