如何确保线程正确解锁



根据我的读数,在此代码中,解锁并不能确保线程以
与锁定顺序相同的顺序解锁。这种说法对吗? 确保线程解锁的最佳同步方案是什么 按照相同的顺序,它们被锁定了?。

- (void)insert:(id)number
{
[_lock lock];
[_numbers insertObject:number];
[_lock unlock];
}
- (void)insert
{
@synchronized (self) {
[_numbers insertObject:number];
}
}

确保线程以与锁定顺序相同的顺序解锁的最佳同步方案是什么?

最好的同步方案是首先不使用锁。请改用中央调度。串行队列执行锁的作用,连贯而简单,并且出错的可能性大大降低。

在回答您的问题时,锁通常是"公平的"(任何给定的线程都不会被饿死(。值得注意的例外是os_unfair_lock(它就在名称中(。但是引入不公平的锁是有原因的,即它们的效率。在 Swift 3 WWDC 视频中使用 GCD 并发编程中说明了使用不公平的锁进行同步。但通常,在大多数同步方案中,锁的公平性不是问题。

话虽如此,苹果建议放弃锁,而是与GCD同步。锁"更容易被滥用"。正如该视频继续所说:

使用 GCD,您的代码将以作用域内的方式运行,这意味着您不能忘记解锁。另一件事是,队列实际上与调试工具中的 Xcode 中的运行时集成得更好。

GCD消除了关于同步公平性的任何歧义,因为它显然是严格的FIFO。

因此,简单的解决方案是使用 GCD 串行队列。更高级的解决方案是使用读取器-写入器模式,该模式通过并发 GCD 队列实现,其中一个队列同步执行读取(但相对于该队列上的其他读取同时执行(,但使用屏障异步执行写入(对于该队列上的任何其他内容不并发(。

最重要的是,虽然我们经常使用 GCD 进行简单的同步,但您的锁是一种完全合适的机制。@synchronized指令很简单,但您看不到它的使用量,因为它的效率略低(尽管只有在执行数百万次同步时才可观察到(。

我可能会推荐 Mike Ash 的文章 Locks, Thread Safety, and Swift: 2017 Edition。同样,不要被文章或上述视频的Swiftiness吓倒,因为其中大部分同样适用于Objective-C。


不用说,如果可以的话,你应该完全最小化同步。正如Apple在他们的线程编程指南:同步中指出的那样:

完全避免同步

对于您处理的任何新项目,甚至对于现有项目,设计代码和数据结构以避免同步是最佳解决方案。尽管锁和其他同步工具很有用,但它们确实会影响任何应用程序的性能。如果整体设计导致特定资源之间的高争用,则线程可能会等待更长的时间。

实现并发的最佳方法是减少并发任务之间的交互和相互依赖关系。如果每个任务都在其自己的私有数据集上运行,则不需要使用锁来保护该数据。即使在两个任务共享一个公共数据集的情况下,您也可以查看对该集进行分区或为每个任务提供自己的副本的方法。当然,复制数据集也有其成本,因此在做出决定之前,您必须权衡这些成本与同步成本。

最新更新