我理解的是,如果在C#中使用通用列表(List),它可以支持多个并发读取器但只有一个写入器。而当引入一个写入器时,必须提供同步构造以使操作线程安全。
List.Contains被认为是读取操作吗?换句话说,如果我调用这个方法,是否需要担心写入器可能同时写入这个List?
我理解的是,如果在C#中使用通用列表(List),它可以支持多个并发读取器但只有一个写入器。而当引入一个写入器时,必须提供同步构造以使操作线程安全。
List.Contains被认为是读取操作吗?换句话说,如果我调用这个方法,是否需要担心写入器可能同时写入这个List?
是的,您应该这样做。基本上,如果列表可能同时用于写入,则我会为任何操作进行同步。
通常,我发现集合分为两类 - 一种是创建、初始化后再也不会改变的(线程安全),另一种是随着时间的推移而发生变化的(不是线程安全的,在所有访问时都需要锁定)。
public bool Contains(T item)
{
if (item == null)
{
for (int j = 0; j < this._size; j++)
{
if (this._items[j] == null)
{
return true;
}
}
return false;
}
EqualityComparer<T> comparer = EqualityComparer<T>.Default;
for (int i = 0; i < this._size; i++)
{
if (comparer.Equals(this._items[i], item))
{
return true;
}
}
return false;
}
正如您所看到的,这是一个简单的遍历操作,绝对是一个“读取”操作。如果您仅用于读取(并且没有任何变异项),则无需锁定。如果您在单独的线程中开始修改列表,则必须同步访问。
List<T>.Contains
绝对是一个读取操作。当您读取它时,可能会有其他线程正在向集合中写入数据。
是的,你必须担心! List.Contains方法只是获取一个EqualityComparer并循环遍历列表中包含的所有项,将传递的参数与当前迭代索引处的项进行比较,因此如果在迭代时修改了列表,则结果可能是不可预测的。
public bool Contains(T item)
{
if (item == null)
{
for (int j = 0; j < this._size; j++)
{
if (this._items[j] == null)
{
return true;
}
}
return false;
}
EqualityComparer<T> comparer = EqualityComparer<T>.Default;
for (int i = 0; i < this._size; i++)
{
if (comparer.Equals(this._items[i], item))
{
return true;
}
}
return false;
}
如果一个写入者可能同时进行写入操作,List.Contains绝对不是线程安全的。您需要用锁包装它以及任何其他的读取和写入操作。
这被视为读操作。您不会遇到任何竞争条件,但如果您关心获取最新信息,可以将List
设为volatile
。
ReaderWriterLock类似乎是为您寻找的同步而构建的。此类型的公共静态成员(在Visual Basic中为Shared)是线程安全的。任何实例成员都不能保证是线程安全的。