【问题标题】:Locking / Concurrency Issue锁定/并发问题
【发布时间】:2011-12-02 13:15:11
【问题描述】:

我有以下 C# 代码:

1.    List<BandEdge> bandEdgeList;
2.    
3.    bandEdgeList = CicApplication.BandEdgeCache.Where(row => row.Coater == coater).ToList();
4.    foreach (BandEdge bandEdge in bandEdgeList)
5.       {
6.          ...
7.          ...
8.       }

我的问题是这样的。一旦在第 3 行填充了“bandEdgeList”,如果另一个线程修改了 CicApplication.BandEdgeCache 的内容,“bandEdgeList”的内容是否会失效?我锁定了 CicApplication.BandEdgeCache getter/setter。但我想知道是否应该锁定这段代码,以便在使用“bandEdgeList”时 CicApplication.BandEdgeCache 的内容不会改变。

【问题讨论】:

  • 如果 BandEdgeCache 也是一个可编辑的集合,那么您可能实际上会在本地列表中丢失项目,可能有项目不满足您的 Coater 条件,或者可能在您的列表中除了下面的答案中的问题之外,它们不再在 BandEdgeCache 集合中。

标签: c# multithreading concurrency locking


【解决方案1】:

不是自动的,但这仍然不是线程安全的。它可能会抛出一个InvalidOperationException

一旦调用ToList,它就会保存这些引用的副本。但是,如果另一个线程在发生这种情况时修改了BandEdgeCache,就会发生不好的事情。

因此,您应该锁定对BandEdgeCache 的所有引用。

但是按照保存列表的思路,这将是安全的,但修改任何 BandEdge 在没有锁定的情况下不是线程安全的。

【讨论】:

    【解决方案2】:

    bandEdgeList 将是一个独立副本(因为您使用的是ToList()),因此您不需要锁定。

    但是,正如@Daniel A. White 所说,您需要锁定创建该副本的 LINQ 语句。

    【讨论】:

    • 这将是一个独立的副本,但为了制作它,BandEdgeCache 需要被锁定。
    • 我今天学到了一些东西...我认为 ToList 实际上并没有复制,您必须使用 ToArray 来复制...谢谢 Oded。
    • @jsobo - 它只复制引用。
    【解决方案3】:

    如果 CicApplication.BandEdgeCache 返回对集合的引用,则在 CicApplication.BandEdgeCache 的 getter 中加锁对您没有帮助。

    CicApplication.BandEdgeCache{
      get{lock(_myCollection){return _myCollection;}}
    }
    

    返回引用,但是一旦返回就退出了锁,因此对 getter 返回的集合的引用使用 Where() 函数是在锁之外完成的,并且不是线程安全的。另一个线程可以很高兴地更改集合,而 Where is 迭代因为没有持有锁 - Daniel 是正确的,如果另一个线程在您生成列表时更改集合,则会引发 InvalidOperationException。

    生成列表后,可以更改原始集合,而不会损害对新列表的访问。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-10-18
      • 1970-01-01
      • 2014-01-11
      • 1970-01-01
      • 1970-01-01
      • 2011-05-20
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多