我有以下代码,并想知道它是否是线程安全的.我只在我从集合中添加或删除项目时锁定,但在迭代集合时不锁定.迭代时锁定会严重影响性能,因为该集合可能包含数十万个项目.有什么建议可以使这个线程安全吗?
谢谢
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万个项目的性能开销实际上非常小.
解决方法:
不,它不是线程安全的,因为当你查看它时可以修改集合……你能做什么:
Item[] items;
lock (myListLock)
{
items = _items.ToArray();
}
foreach (var item in items)
{
var dp = item.DataPoint;
}
所以你在锁之前复制锁中的集合.这显然会使用内存(因为你必须复制List<>)(ConcurrentBag<> .GetEnumerator()几乎就是这样)
请注意,这只有在Item是线程安全的情况下才有效(例如因为它是不可变的)