4

我想实现一个线程安全的MutableDataContainer。当然,我可以简单的加个锁进行读写操作,但是在我的项目中,我需要经常读取数据,所以我不想使用这种方式。

有谁知道如何NSCache实施?

4

1 回答 1

5

我不完全确定 NSCache 专门使用的锁定机制,但我会建议 5 种解决方案之一,特别是在 Apple 平台的现代开发中,这可能是实现 NSCache 的方式之一。有超过 5 种可能,此列表并不详尽。我只是不包括更多,因为在我看来,我不相信 Apple 会使用此处未包括的方法。

以下所有方法都有效,但我会尽量避免听起来我对哪种方法更好有意见(我们将看看它是如何进行的),因为这不是你的问题的真正意义。另外,关于这个问题有很多意见,我不想煽风点火。我将要介绍的是,基于这些方法和我对 Apple 的看法,就我认为 NSCache 的实现方式提供我的看法。

  1. pthread 库的 pthread RW 锁。这是专门为同步而设计的,并且具有与简单互斥锁(RW 锁实际使用的功能)不同的锁所能提供的尽可能多的性能。对于读取,您使用 获得一个锁pthread_rwlock_rdlock,对于写入,您使用 获得它pthread_rwlock_wrlock。在下面,这本质上是一个递归锁,实际上不应该阻塞读取,直到有写入。这优化了更常见的读取路径,并安全地考虑了不太常见的写入路径。对于更不常见的读/写操作,您必须将其本质上提升为写操作。使用 pthread 锁(或互斥锁)时,您可以根据自己的代码逻辑和内部明确定义的条件来优化您的锁定,尽管我不会在这里讨论。

  2. GCD 库的串行队列 - 使用串行队列时,可disaptch_sync用于读取和dispatch_async写入。这与针对公共路径优化的 pthread RW 锁不同,因为读取和写入都是串行的。尽管它确实依赖于 GCD 来通过其底层队列优化来优化读取(例如,GCD 确定何时为dispatch_sync调用适当地阻塞)。对于更不常见的读/写操作,您只需将其视为 read and use dispatch_sync。又是这样,因为这种方法与前一种方法有一个重要区别,即虽然读取应该通过 GCD 快速和优化,但它们仍然是串行的,技术上仍然相互阻塞。

  3. GCD 库的并发队列 - 使用并发队列时,您可以dispatch_sync用于读取和dispatch_barrier_async写入。对于读/写的特殊情况,dispatch_barrier_sync几乎是专门为此目的而制作的。在这个世界中,读取永远不会相互阻塞,并且在遇到障碍之前根本不会阻塞,该障碍是为写入和读/写保留的。这种方法也依赖于 GCD 来优化读取,但是通过使用并发队列,您可以选择退出串行读取。因此,基本上由您来控制必须串行的事物,并在这些操作需要时使用屏障。

  4. Foundation 框架NSLock- 这是 Foundation 框架的经典构造。尽管它的原始实现可能不同,但最近我查看了它的内部构造,并且在我测试的所有情况下,它本质上是一个围绕相应 pthread 实现的 Objective-C 包装器。

  5. Objective-C's @synchronized()- 这是一个内置的 Objective-C 函数/关键字,提供锁定和异常处理。它的底层本质上是一个带有异常处理程序的 NSLock。使用它时,它既用于读取又用于写入。在内部,它们的处理方式相同,因此没有针对另一个进行优化。@synchronized(obj)您的锁与您传递给函数的对象相关联。因此,本质上,您锁定了对该对象的访问,无论它在相应的“@synchronized(){}”块中如何使用或变异。

每种方法都有更多的细节和注意事项,但如果我是一个赌徒(我不是),并且基于我将在此之后进行的几件事,我会说 Apple 正在使用方法 #3 用于 NSCache .

原因 1:Apple 关心性能,尤其是在 Foundation 方面。自 GCD 成立以来,他们就对 GCD 进行了大量投资,并将继续对其进行投资。创建 GCD 的大多数原因是基于在不牺牲性能的情况下尽可能多地删除样板。我会说 Apple 可能在 Foundation 内部的一些地方对他们的建议进行了狗食,而 NSCache 可能就是其中之一。

原因2:类似于“原因1”,最近有证据表明苹果对GCD的投资和对.的投资不足@synchonized(),这在Swift中可以看到。GCD 是 Swift 中首选的同步方法。

于 2018-07-02T05:51:22.053 回答