C# - Lock在哪里适合在库中使用?

时间:2013-08-07 09:36:57

标签: c# multithreading locking

我正在为多个项目编写一个库。它包含包含和操作数据的类。所以我认为我应该锁定这些数据,同时执行这些类的方法。但我还想过,如果必须完成线程安全的数据操作,那么让更高的应用程序层处理锁定。稍后实际应用程序使用类的最佳做法是什么。

假设我有一个班级SpecialList。我该怎么做:

  • 在调用方法时锁定数据
  • 让用户锁定列表,如果他需要线程安全+忽略(索引)异常,那么用户必须抓住它们

如果我知道.NET Framework类List如何处理,我会做同样的事情。

<小时/> 这些类没有明确针对单线程或多线程。他们只是帮助各种用途的课程。

2 个答案:

答案 0 :(得分:5)

如果您正在设计需要保证线程安全的数据结构和类型,那么请确保将锁和其他构造放入这些类型中以维护这些保证。

然而,单靠锁是不够的。

拿一本简单的字典。假设您要确保字典内的内部数据结构不会被多个线程损坏,因此您需要引入锁。但是,如果外部代码执行此操作:

if (!dict.ContainsKey(key))
    dict.Add(key, value);

然后不能保证在调用ContainsKeyAdd之间,某些其他线程尚未将该键添加到字典中。

因此,线程安全类型可能需要比简单锁定更多的东西。可能需要一种方法,可以在原子操作中安全地将键和值添加到字典中,然后返回一个告诉你它做了什么的标记。

事实上,请看这里:ConcurrentDictionary.TryAdd

我的建议是:

  1. 如果您需要这些保证,请通过完成需要安全,可预测且具体实施的方案来设计类型为线程安全的
  2. 如果您不需要这些保证,请不要做任何事情,而只是记录该类型不是线程安全的,将其留给使用它的代码
  3. .NET类型List不以任何方式使用锁,除了一些关注线程安全的选择位置,特别是SyncRoot property

    此外,编写良好的线程安全数据类型并不容易。只需在你需要的地方放入锁,可能使其更加线程安全,但你会在性能方面付出沉重的代价。如果程序在使用它时不需要它是线程安全的,那么你仍然需要付出很多代价。

    编写高性能的线程安全类型更难,并且通常不依赖于锁(单独),而是使用诸如旋转等待,特定CPU指令(其中一些可用于.NET代码)等等。这需要有关现代CPU如何执行代码的高度专业化知识。

    如果我是你,我会给专家留下线程安全,并将它从你自己的类型中删除,除非你绝对需要它。

答案 1 :(得分:1)

我认为答案取决于您想要您的库是否是线程安全的。这似乎是微不足道的,但这就是它的完成方式。考虑例如.NET的集合:

SynchronizedCollection

List(和其他基本集合 - 不是线程安全的)

所以一个好的选择似乎是两个版本,或者只是线程安全版本。