2010-05-12 33 views
12

我打算在静态构造函数中创建一次列表,然后让该类的多个实例同时读取(并枚举)它,而不进行任何锁定。C#列表的线程安全<T>读者

在这篇文章中 http://msdn.microsoft.com/en-us/library/6sh2ey19.aspx MS介绍线程安全的问题如下:

公共静态(在Visual Basic中的Shared)这种类型的 成员都是线程安全的。 任何实例成员不是 保证是线程安全的。

只要 集合未被修改,列表可同时支持多个阅读器 。枚举集合 本质上不是线程安全的 过程。在枚举的 与一个或多个 写入访问竞争的罕见情况下,确保 线程安全的唯一方法是在整个枚举中锁定 集合。要允许 由多个线程访问 读取和写入,您必须执行您自己的同步 。

The “通过集合枚举本质上不是线程安全的过程。” 声明是我担心的。

这是否意味着它是线程安全的读者唯一的情况下,但只要你不使用枚举?

或者对我的情况安全吗?


感谢您的回答。 为什么我需要使用AsReadOnly,如果它可以使用或不使用它?

回答

1

这是否意味着它是线程安全的 读者仅情形,但只要 只要你不使用枚举?或者是 它对我的情况安全吗?

完全取决于您何时写入集合。这与阅读(枚举)没有某种锁定方案不相符。

所以,如果你填充一次,然后只遍历它,你是安全的。但是当一个线程更改列表(添加或删除项目)时,您将需要例如ReaderWriterLockSlim。

当您更改存储的项目的状态时,线程安全与该项目(不是列表)。

8

他们的意思是,如果你列举的集合,而在不同的线程(或自己的线程)改变它,你就会有问题。

只要你根本不改变集合,只要你不跨线程共享IEnumerator,你就不会有任何问题。

+0

通过'不要共享IEnumerators跨线程'你的意思是'不从多个线程访问**相同的枚举器实例**? – 2014-07-08 07:25:32

+1

@EugeneBeresovksy:的确如此。 – SLaks 2014-07-08 13:57:35

3

是的,列表是安全的读者只有的情况下,如果列表永远不会被修改,那么它会没事的。

如果事实上是在施工后列表不会被修改,那么你应该使用更合适的接口,如ReadOnlyCollection。如果它被存储在一个公共静态变量中,如你所说,你应该使用该接口。

private static List<T> shared_list; 

private static ReadOnlyCollection<T> _data; 
public static IEnumerable<T> Data 
{ 
    get 
    { 
     return _data ?? (_data = shared_list.AsReadOnly()); 
    } 
} 

==== ====编辑

这个版本缓存更快的未来查找时间的ReadOnlyCollection参考。

请注意,如果两个线程在空值时尝试同时获取引用,但是因为所有内容都是只读的,这并不重要,所以我们将只在_data变量上发生数据竞争创建一个额外的ReadOnlyCollection对象,与同步相比,它便宜。

+0

请注意,您仍然不能更改原始列表。另外,每次你获得财产时都不要调用'AsReadOnly'。 – SLaks 2010-05-12 22:17:23

+0

AsReadOnly是根据MSDN文档的O(1)操作。我相信它只是简单地委托给底层集合的ReadOnlyCollection包装。所以AsReadOnly应该很快,但它确实会导致分配,所以我们可能会共享该内存。虐待编辑来反映。 – luke 2010-05-12 22:58:11

+0

在.NET 4下,您可以使用通用的Lazy类进行初始化。请参阅http://msdn.microsoft.com/en-us/library/dd642329.aspx – TrueWill 2010-12-17 02:21:45