部分线程安全的字典

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

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

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

现在,我有以下内容:

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

有没有办法这样做而不需要多次复制集合?

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

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


除非我遗漏了某些内容,否则我相信您可以将您的值公开为IEnumerable而无需使用yield关键字复制值:

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

但是请注意(我猜你已经知道了),这种方法提供了惰性求值 ,这意味着上面实现的Values属性不能被视为提供快照

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

 var d = new ThreadSafeDictionary(); // d is empty right now IEnumerable 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)

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

你有三个选择。

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

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

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

如果您想要了解字典当前状态的快照 ,那么您可以使用此集合类型进行其他任何操作。 这与ConcurrentDictionary.Values属性使用的技术相同。

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

查看下一个可能性,只显示ICollection接口,因此在Values()中可以返回自己的实现。 此实现仅使用Dictioanry.Values上的引用,并始终使用ReadLock访问项。