List<T> 对于读取是线程安全的吗?

37

以下伪代码是线程安全的吗?

IList<T> dataList = SomeNhibernateRepository.GetData();

Parallel.For(..i..)
{
    foreach(var item in dataList)
    {
       DoSomething(item);
    }
}
列表永远不会被改变,只是并行地进行迭代和读取。没有对字段进行任何写入操作或类似的操作。

谢谢。

4个回答

60

是的,List<T> 在多个线程同时读取时是可以的,只要没有写入操作。

根据文档

List<T> 可以支持多个读取者并发访问,只要集合不被修改。

编辑:请注意,您的代码不一定使用 List<T> - 只是一个 IList<T>。您知道 GetData() 返回的类型吗?如果您对 GetData() 有控制权,那么您可能需要记录返回的列表在读取时是线程安全的,如果它实际上返回的是 List<T>


在单例方法内调用实例变量 List 的 find() 方法怎么样?结果会不会被错误地返回给其他用户? - eaglei22
@eaglei22:恐怕我不知道你的意思 - 我建议你提出一个带有更多上下文的新问题。但从根本上说,如果您有多个线程在没有保护的情况下修改单个List<T>,那就是一个问题,我怀疑仅仅是“查找”是一个问题。 - Jon Skeet
谢谢Jon。对于混淆感到抱歉。如果两个用户访问单例类中的一个方法: GetCode(string code) => return conditionCodes.Find(x => x.Code.Equals(code)); List对象“conditionCodes”只会被填充,而不会被更改。并发访问是否安全?如果仍然有疑惑,我将添加一个新问题。谢谢。 - eaglei22
1
@eaglei22:是的,这是多个读取器没有修改的情况,所以应该没问题。 - Jon Skeet

3
只要DoSomething(item)不修改dataList,它就是完全线程安全的。既然您说没有修改,那么它是线程安全的。

2
为确保没有人更改您的列表,您可以通过 IEnumerable 访问它。
IEnumerable<T> dataList = SomeNhibernateRepository.GetData();

Parallel.For(..i..)
{
    foreach(var item in dataList)
    {
       DoSomething(item);
    }
}

这仍然可以转换回List<T>。你可以返回List<T>.AsReadOnly(),但即使如此,原始列表也可以通过反射访问。 - jakobbotsch
2
如果您将List<T>强制转换回去,仍然无法修改原始列表。 如果您考虑反射,您也可以规避运行时检查。 如何使字段私有?您可以使用private关键字回答。我可以用反射来访问它。 - Massimiliano Peluso

-1
如果你所说的是正确的,那我会这么说。但是你所说或想的可能并不是现实中发生的事情。你如何在代码中表达你所说的话。如何强制执行 List 永远不被修改的约束条件?

3
相对来说,这很容易:将其设为类中的私有成员,该类仅执行对列表的读取访问,可能通过其他成员公开值。需要控制好该类,但只需查看单个代码块即可。 - Jon Skeet

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