【问题标题】:C# - Where is Lock Appropriate to Use in Libraries?C# - 锁在哪里适合在库中使用?
【发布时间】:2013-08-07 23:43:23
【问题描述】:

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

假设我有一门课SpecialList。我该怎么办:

  • 调用方法时锁定数据
  • 如果用户需要线程安全 + 忽略(索引)异常,则让用户锁定列表,以便用户必须捕获它们

?

如果我知道 .NET Framework 类 List 的处理方式,我也会这样做。


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

【问题讨论】:

  • 除非您的列表具有专门针对多线程场景的功能,否则让所有用户为他们可能永远不需要的锁付费是没有任何意义的。 List<T> 本身不是线程安全的(文档中有一个“线程安全”部分进行了解释)。
  • 不存在不针对多线程的。这是否意味着我在低级别我应该避免锁定?
  • 是的,您应该避免使用锁。相反,只需记录线程 un-safety.

标签: c# multithreading locking


【解决方案1】:

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

但是,仅靠锁是不够的。

拿一本简单的字典。假设你想确保字典内部的数据结构不会被多个线程破坏,所以你引入了锁。但是,如果外部代码这样做:

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

那么这里不能保证在对ContainsKeyAdd 的调用之间,其他一些线程还没有将该键添加到字典中。

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

其实看这里:ConcurrentDictionary.TryAdd

我的建议是这样的:

  1. 如果您需要这些保证,请通过需要安全且可预测的场景将类型设计为线程安全的,并具体实施这些场景
  2. 如果您不需要这些保证,请不要费心做任何事情,只需记录该类型不是线程安全的,将其留给使用它的代码

.NET 类型 List 不以任何方式使用锁,除了少数需要考虑线程安全的地方,特别是 SyncRoot property

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

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

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

【讨论】:

  • 我看不出锁对于线程安全来说是不够的。你能给我一些添加到锁定的关键字,以便我可以阅读更多关于它的信息吗?
  • 线程安全!=没有数据损坏。线程安全类型实现了必须是线程安全的场景。正如我所说,查看 ConcurrentDictionary 上的 TryAdd 方法,您也可以在 google 上搜索“concurrentdictionary tryadd”并阅读一些文章。
【解决方案2】:

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

SynchronizedCollection

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

所以一个不错的选择似乎是同时做两个版本,或者不是只做线程安全的版本。

【讨论】:

    猜你喜欢
    • 2017-01-17
    • 1970-01-01
    • 1970-01-01
    • 2010-09-16
    • 2013-06-29
    • 1970-01-01
    • 1970-01-01
    • 2011-12-21
    • 1970-01-01
    相关资源
    最近更新 更多