线程安全访问静态集合

时间:2011-11-10 21:20:41

标签: c# collections thread-safety

我正在创建一个具有某种键值配对的类。目前,我有类似于以下内容:

private static Dictionary<Type, List<PropertyInfo>> PropertyCache { get; set; }

这是以线程安全方式实现的正确方法吗?由于某种原因,在这种情况下,staticDictionary的影响有些让我烦恼。如果这是正确的,是否有一种不正确的方式可以证明,以便我可以在我的其余代码中避免它。

此外,它应该是只读的(我只会添加 - 从集合中删除东西)?

如果它有所不同,则该属性将始终声明为private

提前致谢

虽然标记为C#,但VB中的答案还可以,我是“双语”可以这么说

修改

在与Jon Skeet的回答评论中讨论后,用法将是:

// I will do this
PropertyCache.Add(typeof(string), new List<PropertyInfo>());
PropertyCache.Remove(typeof(string));

// I will never do this 
PropertyCache = new Dictionary<Type, List<PropertyInfo>>();

我永远不会遍历集合,只能通过密钥访问。

3 个答案:

答案 0 :(得分:3)

除非您需要更改变量的值,否则您应该将其设为只读,是的。

如果您正在使用.NET 4,则应考虑使用ConcurrentDictionary - 否则,我可能会编写只为每次访问获取锁定的访问者。虽然您可以使用ReaderWriterLockSlim等,但我个人会采用最简单的安全方法开始。

请注意,以线程安全的方式迭代字典的内容将是棘手的 - 希望您不需要它。

答案 1 :(得分:1)

使成员static使其比实例成员更有可能具有跨线程访问权限。请记住,static并不意味着多个线程可以安全地使用某些东西,这意味着该对象在定义它的对象的多个实例之间共享。

可以强制实现ICollection接口的集合,以访问可用于SyncRoot对象的lock,以确保一次只有一个线程可以执行一系列指令会导致集合发生变化。 Using SyncRoot for Multithreaded Applications

lock(((ICollection)myObject).SyncRoot)
{
    //Code that should be executed by only one concurrent thread
    //This is add/insert/remove/iterate/clear/etc.
}

除了本手册(或老式学校方式)之外,.NET 4中还有可用的并发对象,基本上可以执行此操作,但需要进行一些其他特殊检查。在大多数情况下,这些对象都针对性能进行了优化,因为您可以获得完全安全的对象。如果你在对象上使用了一组非常受控制的小动作(你有1个add,1个remove方法而且从不枚举整个集合,只是访问特定的已知条目)你可以使用更轻量级的{{1}以上示例以获得更好的性能。

特别是在一次向集合中添加多个对象时,您会看到这一点。使用并发对象,每个Add操作都是原子的,具有锁定和解锁功能。如果你在一个紧凑的循环中运行它,你会失去一些性能来反复获取锁。如果另一个线程正在尝试读取,则访问争用会更高一些。如果您自己使用lock语句,则可以获取锁定,以快速,紧密的循环添加对象,然后释放锁定。想要访问该对象的线程将等待一段时间,但整体操作应该更快完成。另外请记住,差异通常很低,不值得,几乎在所有情况下都属于过早优化的范畴。

答案 2 :(得分:0)

你所展示的绝不是线程安全的。如果多个线程可以同时插入或删除项目,则可能存在问题。