c# - 在 C# 中使用数据库值填充 ConcurrentDictionary 的锁定注意事项

标签 c# multithreading generics thread-safety

当我在这里填写我的 ConcurrentDictionary 时是否需要使用 lock(lockObj){} block ?对于一些背景知识,这将在 MVC 应用程序中使用,但我怀疑场景问题与任何多线程应用程序相关。

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

1) 将 List 对象的值设置为私有(private)静态值是否更好,您将其锁定以希望在填充 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();

最佳答案

你得到的肯定是不安全的。考虑:

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

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

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

如果您在第一次加载后永远不需要更新字典,您甚至可能只需要 Lazy<Dictionary<string, string>> 就可以了。 - 拥有多个读者是安全的,只要没有作者。我相信 Lazy<T>将适本地处理内存障碍。

关于c# - 在 C# 中使用数据库值填充 ConcurrentDictionary 的锁定注意事项,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9843970/

相关文章:

generics - 函数返回值的Kotlin默认泛型参数

c# - 将字符串分配给 HtmlElement 对象的 InnerHtml

c# - 当发出同步 I/O 调用时,当前线程 (C#) 的 ThreadState 是什么?

c++ - 生成 C++ 进程的线程转储

java - 在 Java 中将泛型类型作为泛型方法参数传递

java - 通过反射获取固定泛型参数的父方法

c# - 在c#中将html转换为json

c# - 如何按长度降序对字符串列表的内容进行排序?

c# - 将 "nameof"关键字与仅设置属性一起使用

java - 如何实现一个单独的线程,在主循环运行时评估用户输入?