【发布时间】:2015-06-15 08:08:28
【问题描述】:
我有以下代码,想知道它是否是线程安全的。我只在从集合中添加或删除项目时锁定,但在迭代集合时不锁定。迭代时锁定会严重影响性能,因为集合可能包含数十万个项目。有什么建议可以使这个线程安全吗?
谢谢
public class Item
{
public string DataPoint { get; private set; }
public Item(string dataPoint)
{
DataPoint = dataPoint;
}
}
public class Test
{
private List<Item> _items;
private readonly object myListLock = new object();
public Test()
{
_items = new List<Item>();
}
public void Subscribe(Item item)
{
lock (myListLock)
{
if (!_items.Contains(item))
{
_items.Add(item);
}
}
}
public void Unsubscribe(Item item)
{
lock (myListLock)
{
if (_items.Contains(item))
{
_items.Remove(item);
}
}
}
public void Iterate()
{
foreach (var item in _items)
{
var dp = item.DataPoint;
}
}
}
编辑
我很好奇,并再次分析了未锁定迭代与在 myListLock 上的锁定内迭代之间的性能,并且锁定超过 1000 万个项目的迭代的性能开销实际上非常小。
【问题讨论】:
-
我知道这可能不是您要查找的评论,但您是否知道Concurent Dictionary 是线程安全的?
-
如果您正在修改集合,则需要禁用迭代,因为如果集合在迭代过程中被修改,foreach 会引发错误。
-
@Thomas,是的,但它也严重影响了我运行的一些测试中的性能
-
处于调试模式?有时,安全地跑比不系安全带的快跑要慢一点……你不觉得吗?
-
如果您的锁争用非常低(“非常低”的意思是“profile and see”),那么并发收集确实会表现得更慢。此外,迭代并发集合还会在所有存储桶上获得完全锁定,并在迭代之前创建一个单独的列表(类似于下面的@xanatos 答案)——这不是字典最适合的方法。但与在
Iterate内部完成的实际处理相比,这种性能损失可能可以忽略不计。
标签: c# multithreading collections concurrency locking