我正在为多个项目编写一个库。它包含包含和操作数据的类。所以我认为我应该锁定这些数据,同时执行这些类的方法。但我还想过,如果必须完成线程安全的数据操作,那么让更高的应用程序层处理锁定。稍后实际应用程序使用库类的最佳做法是什么。
假设我有一个班级SpecialList
。我该怎么做:
如果我知道.NET Framework类List
如何处理,我会做同样的事情。
答案 0 :(得分:5)
如果您正在设计需要保证线程安全的数据结构和类型,那么请确保将锁和其他构造放入这些类型中以维护这些保证。
然而,单靠锁是不够的。
拿一本简单的字典。假设您要确保字典内的内部数据结构不会被多个线程损坏,因此您需要引入锁。但是,如果外部代码执行此操作:
if (!dict.ContainsKey(key))
dict.Add(key, value);
然后不能保证在调用ContainsKey
和Add
之间,某些其他线程尚未将该键添加到字典中。
因此,线程安全类型可能需要比简单锁定更多的东西。可能需要一种方法,可以在原子操作中安全地将键和值添加到字典中,然后返回一个告诉你它做了什么的标记。
事实上,请看这里:ConcurrentDictionary.TryAdd。
我的建议是:
.NET类型List
不以任何方式使用锁,除了一些关注线程安全的选择位置,特别是SyncRoot property。
此外,编写良好的线程安全数据类型并不容易。只需在你需要的地方放入锁,可能使其更加线程安全,但你会在性能方面付出沉重的代价。如果程序在使用它时不需要它是线程安全的,那么你仍然需要付出很多代价。
编写高性能的线程安全类型更难,并且通常不依赖于锁(单独),而是使用诸如旋转等待,特定CPU指令(其中一些可用于.NET代码)等等。这需要有关现代CPU如何执行代码的高度专业化知识。
如果我是你,我会给专家留下线程安全,并将它从你自己的类型中删除,除非你绝对需要它。
答案 1 :(得分:1)
我认为答案取决于您想要您的库是否是线程安全的。这似乎是微不足道的,但这就是它的完成方式。考虑例如.NET的集合:
List(和其他基本集合 - 不是线程安全的)
所以一个好的选择似乎是两个版本,或者只是线程安全版本。