【问题标题】:Ensuring thread safe operations for a list that is value in a dictionary确保对字典中的值列表进行线程安全操作
【发布时间】:2013-01-17 11:36:49
【问题描述】:

我有一个Dictionary<object1, List<object2>(我为我的结构做的抽象,有点复杂)。

字典中的列表是在非并行上下文中初始化的,但是向列表中添加新元素需要线程安全的方法。不会删除项目(字典对象将在事务结束时处理)。 此外,键值对仅在初始化时添加,因此在工作期间不会从字典中添加或删除键,仅更新值。

我不能使用并发集合,我被困在一个旧的 .NET 框架中。

起初我用ReadWriteSlimlock 锁定了整个字典。好吧,这是非常糟糕的表现。有许多添加操作发生,他们只是等待另一个。仅锁定每个列表是一个更好的解决方案,因为至少我正在为每个键并行做一些事情。 另外,加法操作也不是简单的list.Add(object2),加法时还需要在线程安全区进行一些复杂的操作。

但我不知道实现它的最佳方法是什么:

  1. lock(dictionary.Value)?
  2. 使用ReadWriteSlimlock 的字典(每个键一个)?
  3. 还有其他更好的解决方案吗?

【问题讨论】:

    标签: c# multithreading thread-safety locking


    【解决方案1】:

    另一种解决方案可能是实现您并发的List<object> 类。 类似的东西:

    public class ConcurentList {
        private object sync = new object(); 
        private List<object> realList = new List<object>(); 
    
    
        public void Add(object o) {
            lock(sync){
               realList.Add(o);
            }
        }   
    
        /** ADD OTHERE METHODS IMPEMENTATION IF NEED **/
    
    }
    

    在字典中有:

    Dictionary<object1, ConcurentList>
    

    为什么封装而不是扩展List&lt;object&gt;,是因为Add方法不是虚拟的,所以你可以“覆盖”它的唯一方法是使用new关键字,即如果在完全相同的类型上使用,则保证被调用,这意味着如果将列表转换为基础,它将不会被调用,因此孔架构将失败。

    通过封装,您只为调用者提供了一种方法,您可以在其中控制一切。

    不知道这个解决方案是否符合您的需求,但希望它能给您一些关于如何管理内容的提示。

    【讨论】:

    • 由于添加是我的实际问题,我真的很喜欢封装的想法。谢谢你的提示!
    【解决方案2】:

    为什么不编写自己的Dictionary&lt;&gt;List&lt;&gt; 的并发线程安全变体?这是我为 .NET 2 所做的。

    就我个人而言,我选择了ReaderWriterLockSlim,因为它允许您根据访问模式控制锁定,并且在大多数情况下,我的列表是从被写入的更多内容中读取的。

    但是,您正在添加大量内容,因此您可能想同时尝试(lockReaderWriterLockSlim),看看哪个效果更好。

    【讨论】:

    • 在某些时候ReaderWriterLockSlim 在这种情况下使用似乎很奇怪(我正在并行编写,最后只编写一次)。我认为ReaderWriterLockSlim 适用于涉及更多阅读的情况。最后我想我会实现一些想法并比较它们,因为性能是这个问题的一个大问题。
    • 是的,据我了解,它主要针对有读/写需求且存在大量读取争用的地方。
    【解决方案3】:

    如果从列表中读取与写入同时发生,则为每个字典条目构造一个ReaderWriterLockSlim 对象将是最佳解决方案,因为多个读取器将能够在同一个列表上同时进行。它只比使用 lock(key) 稍微困难一点,并且在少数列表比其他列表更频繁地被读取的情况下,它具有提高性能的潜力。

    如果您走ReaderWriterLockSlim 路线,最好将列表和锁包装在一个类中,而不是为读写器锁创建单独的字典:

    class LockableList {
        public ReaderWriterLockSlim RwLock {get;private set;}
        public List<object2> Data {get;private set;}
        public LockableList() {
            RwLock = new ReaderWriterLockSlim();
            Data = new List<object2>();
        }
    }
    ...
    Dictionary<object1,LockableList> myDictionary;
    

    将列表和外观结合起来可以让您查找字典条目一次,根据需要锁定它以供读取或写入,并使用关联的列表进行工作。

    【讨论】:

    • 您将列表和锁包装起来的想法很棒!不仅在这种情况下值得考虑。
    猜你喜欢
    • 2023-01-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-12
    • 1970-01-01
    • 1970-01-01
    • 2011-10-20
    相关资源
    最近更新 更多