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

Bit*_*lue 3 c# multithreading locking

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

假设我有一堂课SpecialList.我该怎么办:

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

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


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

ang*_*son 5

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

但是,单靠锁是不够的.

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

if (!dict.ContainsKey(key))
    dict.Add(key, value);
Run Code Online (Sandbox Code Playgroud)

然后就不能保证在调用ContainsKey和之间Add,某些其他线程还没有将该键添加到字典中.

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

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

我的建议是这样的:

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

.NET类型List不以任何方式使用锁,除了一些关注线程安全的选择位置,特别是SyncRoot属性.

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

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

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