【问题标题】:Thread-safe access to static collection对静态集合的线程安全访问
【发布时间】:2011-12-26 13:14:58
【问题描述】:

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

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>>();

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

【问题讨论】:

  • “我只会从集合中添加-删除东西”是什么意思?
  • @SteveDanner,例如我永远不会去PropertyCache = new Dictionary&lt;Type, List&lt;PropertyInfo&gt;&gt;,只有PropertyCache.Add(item) :)
  • 好的,ReadOnlyDictionary 将无法工作。考虑一下 Jon Skeet 的回答,IMO。
  • 您问:“这是以线程安全方法实现的正确方法吗?”答案是响亮的“不”。标记静态的东西与线程安全无关。它只是将对象标记为在对象实例之间共享。共享。所有实例都可以访问同一个对象。对象本身(或试图访问它的代码)必须在访问时实现线程安全逻辑。

标签: c# collections thread-safety


【解决方案1】:

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

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

请注意,以线程安全的方式迭代字典的内容会很棘手 - 但希望您不需要这样做。

【讨论】:

  • 谢谢乔恩。请您详细说明“最简单的安全方法”的含义,以便我知道如何在 Net 35 应用程序中实现这一点。
  • @SBlackler:最简单的方法就是锁定每次访问。将使用示例进行编辑。
  • @SBlackler:实际上,我刚刚注意到该值是一​​个列表……它不是线程安全的也不是。你打算如何使用它?填充后,列表是只读的吗?
  • 感谢您提醒我们该列表也不是线程安全的。我将重新编辑示例用法,因为我在这里感到困惑......
  • @Jon 好点 - 集合包含集合。有很多机会发生冲突。
【解决方案2】:

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

【讨论】:

    【解决方案3】:

    创建成员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 个添加、1 个删除方法,并且从不枚举整个集合,而只是访问特定的已知条目),您可以使用更轻量级的 lock() 示例以上以获得更好的性能。

    您会看到这一点,尤其是在一次向集合中添加多个对象时。使用并发对象,每个 Add 操作都是原子的,带有锁定和解锁。如果你在一个紧密的循环中运行它,你会因为一遍又一遍地获取锁而失去一些性能。如果另一个线程正在尝试读取,则访问争用会更高一些。如果您自己使用 lock 语句,您可以获取锁,在快速、紧密的循环中添加对象,然后释放锁。想要访问该对象的线程将等待更长的时间,但总体而言,操作应该更快地完成。另请记住,差异通常非常小,不值得这样做,并且几乎在所有情况下都属于过早优化的范畴。

    【讨论】:

      猜你喜欢
      • 2013-12-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-06-15
      • 2014-09-16
      • 2012-09-22
      • 2015-03-24
      • 2011-09-07
      相关资源
      最近更新 更多