我的理解是,如果你在 C# 中使用通用列表(List),它可以支持多个并发读取器,但只能支持一个写入器。而且,当您将编写器引入组合时,您还必须提供同步构造以使操作线程安全。
List.Contains 是否被视为读取操作?换句话说,如果我调用这个方法,我是否需要担心作者可能会同时写入这个列表?
我的理解是,如果你在 C# 中使用通用列表(List),它可以支持多个并发读取器,但只能支持一个写入器。而且,当您将编写器引入组合时,您还必须提供同步构造以使操作线程安全。
List.Contains 是否被视为读取操作?换句话说,如果我调用这个方法,我是否需要担心作者可能会同时写入这个列表?
是的你应该。基本上,如果列表可能同时用于写入,我会同步任何操作。
一般来说,我发现集合分为两类——一类是被创建、初始化然后再也不会改变的(线程安全的),另一类是随时间变化的(不是线程安全的,锁定所有访问)。
如果你使用 Reflector 来检查代码,你会得到这样的结果:
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
.