返回

iOS 中 @synchronized 的双面性:性能杀手还是必要之恶?

IOS

序言

在 iOS 开发的世界中,@synchronized 一直是一个引起争议的话题。作为一种用于保护共享资源免受多线程并发访问的锁机制,它既有优点,也有缺点。本文将深入探讨 @synchronized 的特性和限制,揭示其性能劣势的根源,并阐述苹果为提升其效率而实施的优化措施。

了解 @synchronized

@synchronized 是一种编译器指令,它将被编译成一个称为objc_sync_enterobjc_sync_exit的Objective-C运行时函数。当线程进入 @synchronized 块时,它会获取一个互斥锁,防止其他线程同时访问受保护的代码块。当线程退出 @synchronized 块时,它会释放互斥锁,允许其他线程进入。

优点

  • 简单易用: @synchronized 语法简单,易于理解和使用。
  • 线程安全: 它提供了一种可靠的方式来保护共享资源免受并发访问。
  • 防止死锁: @synchronized 确保同一时刻只有一个线程可以访问受保护的代码块,从而防止死锁的发生。

缺点

然而,@synchronized 也有其缺点:

  • 性能低效: @synchronized 涉及内核调用,这可能导致显著的性能开销。
  • 粒度粗糙: 它锁定整个代码块,即使只有一小部分代码需要同步。
  • 易于滥用: 开发者可能会过度使用 @synchronized,导致不必要的性能损失。

性能劣势的原因

@synchronized 的性能劣势主要源于以下几个原因:

  • 内核调用: 获取和释放互斥锁需要进行系统调用,这比用户空间锁更加耗时。
  • 粒度粗糙: 锁定整个代码块可能会阻止其他线程访问不需要同步的部分。
  • 争用: 当多个线程同时争夺同一个互斥锁时,就会发生争用,导致性能下降。

苹果的优化措施

为了减轻 @synchronized 的性能影响,苹果实施了以下优化措施:

  • 自旋锁: 对于短时间锁定的情况,@synchronized 会使用自旋锁,避免内核调用。
  • 递归锁: @synchronized 允许同一线程多次进入相同的代码块,而无需每次都获取新的锁。
  • 优化的锁实现: 苹果对互斥锁的实现进行了优化,以提高其效率。

何时使用 @synchronized

尽管有其性能缺陷,@synchronized 在某些情况下仍然是有用的:

  • 保护临界区: 当需要保护共享数据免受并发访问时,@synchronized 是一个合适的选择。
  • 防止死锁: 当多个线程访问共享资源时,@synchronized 可以防止死锁的发生。
  • 简化代码: 对于简单的多线程场景,@synchronized 可以提供一种简单有效的方式来确保线程安全。

替代方案

对于需要更高性能或更精细控制锁定的情况,可以使用以下替代方案:

  • NSLock NSLock 是一个基于用户空间的锁,可以提供更好的性能,但需要手动管理。
  • NSRecursiveLock NSRecursiveLock 是一个递归锁,允许同一线程多次获取相同的锁。
  • dispatch_semaphore dispatch_semaphore 是一种轻量级的信号量,可以用于同步多线程访问。

结论

@synchronized 是 iOS 中一种强大的锁机制,但它也有其性能限制。了解其优缺点以及苹果为提升其效率所做的优化措施,对于在实际项目中有效使用 @synchronized 至关重要。对于需要更高性能或更精细控制锁定的情况,可以使用替代方案来满足特定的需求。通过仔细权衡性能和易用性,开发者可以优化其 iOS 应用程序的并发性能。