锁定/并发问题

3

我有以下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集合中,除了下面答案中提到的问题。 - Marc
3个回答

5
并不会自动进行,但这仍然不是线程安全的。它可能会抛出一个InvalidOperationException异常。
一旦调用ToList方法,它会保存那些引用的副本。但如果另一个线程在此期间修改了BandEdgeCache,那么就会发生糟糕的事情。
因此,您应锁定所有对BandEdgeCache的引用。
但是,沿着保存的列表进行操作是安全的,但如果要修改任何BandEdge,则需要进行一些锁定才能保证线程安全。

3
`bandEdgeList` 将是一个独立的副本(因为你使用了 `ToList()` ),所以你不需要锁定。
但是,正如 @Daniel A. White 所评论的那样,你需要在创建该副本的 LINQ 语句周围加锁。

1
这将是一个独立的副本,但为了创建它,BandEdgeCache需要被锁定。 - Daniel A. White
今天我学到了一些东西... 我以为ToList实际上不会复制,而你必须使用ToArray来复制... 谢谢Oded。 - John Sobolewski
1
@jsobo - 它只复制引用。 - Daniel A. White

1
将锁放在CicApplication.BandEdgeCache的getter中对你没有帮助,因为它返回的是集合的引用。
CicApplication.BandEdgeCache{
  get{lock(_myCollection){return _myCollection;}}
}

返回引用,但一旦返回就已经退出了锁定状态,因此在获取器返回的集合上使用Where()函数是在锁定状态之外完成的,不是线程安全的。另一个线程可以在迭代时轻松地更改集合,因为没有保持锁定状态 - Daniel是正确的,如果另一个线程在生成列表时更改了集合,则会抛出InvalidOperationException。

生成列表后,原始集合可以更改而不会影响访问新列表。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接