代码之家  ›  专栏  ›  技术社区  ›  SLaks

部分线程安全字典

  •  3
  • SLaks  · 技术社区  · 14 年前

    我有一个班级 Dictionary 缓存某些数据的实例。

    该类使用 ReaderWriterLockSlim

    我想在类之外公开字典的值。
    什么是线程安全的方法?

    现在,我有:

    public ReadOnlyCollection<MyClass> Values() {
        using (sync.ReadLock())
            return new ReadOnlyCollection<MyClass>(cache.Values.ToArray()); 
    }
    

    有没有办法做到这一点,而不复制收藏多次?

    我正在使用.net 3.5(不是4.0)

    4 回复  |  直到 14 年前
        1
  •  1
  •   Dan Tao    14 年前

    编辑 :我个人认为下面的代码在技术上正确地回答了您的问题(如中所述,它提供了一种在不创建副本的情况下枚举集合中的值的方法)。一些开发商的声誉远比我强烈建议的要高 反对 出于他们在编辑/评论中解释的原因,这种方法。简而言之: 这显然是个坏主意。 所以我留下答案,但建议你不要用它。


    除非我漏掉了什么,我相信你可以把你的价值观 IEnumerable<MyClass> 无需使用 yield 关键字:

    public IEnumerable<MyClass> Values {
        get {
            using (sync.ReadLock()) {
                foreach (MyClass value in cache.Values)
                    yield return value;
            }
        }
    }
    

    但是,请注意(我猜您已经知道了),这种方法提供了 懒惰的评价 ,这意味着 Values 上面实现的属性可以 被视为提供 快照

    换句话说…好吧,看看这段代码(我当然是在猜测你这类代码的一些细节):

    var d = new ThreadSafeDictionary<string, string>();
    
    // d is empty right now
    IEnumerable<string> values = d.Values;
    
    d.Add("someKey", "someValue");
    
    // if values were a snapshot, this would output nothing...
    // but in FACT, since it is lazily evaluated, it will now have
    // what is CURRENTLY in d.Values ("someValue")
    foreach (string s in values) {
        Console.WriteLine(s);
    }
    

    所以如果要求 价值观 属性等同于 快照 什么在里面 cache 在访问属性时,您必须复制。

    (开始280Z28):下面是一个不熟悉“做事方式”的人如何锁定代码的示例:

    IEnumerator enumerator = obj.Values.GetEnumerator();
    MyClass first = null;
    if (enumerator.MoveNext())
        first = enumerator.Current;
    

    (280Z28结束)

        2
  •  9
  •   Eric Lippert    14 年前

    我想在类之外公开字典的值。 什么是线程安全的方法?

    你有三个选择。

    1)复制数据,分发副本。优点:不用担心线程安全访问数据。缺点:客户端得到的是过期数据的副本,而不是最新数据的副本。而且,复印也很贵。

    2)分发一个对象,该对象在读取基础集合时将其锁定。您必须编写自己的只读集合,该集合引用“父”集合的锁。仔细设计这两个对象,这样就不可能出现死锁。优点:从客户的角度来看,它“工作正常”;它们可以获得最新的数据,而不必担心锁定问题。缺点:给你更多的工作。

    3)把问题推给客户。公开锁,并要求客户端在使用之前锁定数据本身上的所有视图。优点:没有你的工作。缺点:为客户做更多的工作,他们可能不愿意或不能做的工作。死锁等风险,现在成了客户的问题,而不是你的问题。

        3
  •  2
  •   Sam Harwell    14 年前

    如果你想 快照 在字典的当前状态中,您确实无法使用此集合类型执行其他操作。这与 ConcurrentDictionary<TKey, TValue>.Values 财产。

    如果你不介意扔一个 InvalidOperationException 如果在枚举集合时对其进行了修改,则可以返回 cache.Values 因为它是只读的(因此不会损坏字典数据)。

        4
  •  0
  •   Dewfy    14 年前

    查看下一个可能性,只公开ICollection接口,因此在values()中可以返回自己的实现。此实现将仅对dictioanry.values使用引用,并始终对访问项使用readlock。