部分线程安全的字典

时间:2010-02-16 16:58:13

标签: c# .net thread-safety

我有一个类,它维护一个缓存某些数据的私有Dictionary实例。

该类使用ReaderWriterLockSlim从多个线程写入字典。

我想在课堂外公开字典的值 什么是线程安全的方法呢?

现在,我有以下内容:

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

有没有办法在不多次复制集合的情况下执行此操作?

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

4 个答案:

答案 0 :(得分:9)

  

我想在课堂外面公开字典的值。   什么是线程安全的方法呢?

你有三个选择。

1)复制数据,分发副本。优点:无需担心线程安全访问数据。缺点:客户获得过时数据的副本,而不是新鲜的最新数据。此外,复制也很昂贵。

2)分发一个在读取底层集合时锁定它的对象。您必须编写自己的只读集合,该集合引用了“父”集合的锁定。仔细设计两个对象,以便无法实现死锁。优点:从客户的角度来看“正常”;他们获得最新数据,而不必担心锁定。缺点:为您做更多的工作。

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

答案 1 :(得分:2)

如果您想要字典当前状态的快照,那么您无法使用此集合类型执行任何其他操作。这与ConcurrentDictionary<TKey, TValue>.Values属性使用的技术相同。

如果您在枚举时修改了集合,如果你不介意抛出InvalidOperationException,那么你可以只返回cache.Values,因为它是只读的(因此不能破坏字典数据)

答案 2 :(得分:1)

编辑:我个人认为以下代码在技术上正确地回答了您的问题(因为它提供了一种枚举集合中的值而无需创建副本的方法)。由于他们在编辑/评论中已经解释过的原因,一些开发人员比我强烈建议反对这种方法更有信誉。简而言之:这显然是一个坏主意。因此我会离开答案,但建议您不要使用它。


除非我遗漏了某些内容,否则我相信您可以将您的值公开为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);
}

因此,如果要求此Values属性等同于访问该属性时cache内的快照,那么您将要去必须复制。

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

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

(结束280Z28)

答案 3 :(得分:0)

回顾下一个可能性,只是公开ICollection接口,所以在Values()中你可以返回自己的实现。此实现仅使用Dictioanry.Values上的引用,并始终使用ReadLock作为访问项。

相关问题