锁
为什么需要在应用层管理多线程读写?
应用层可以根据实际的业务逻辑和需求,有选择性地选择lock和unlock的时机,从而更有效地管理并发,这个管理的过程和业务强绑定。抽象出来描述类似,你正在写的代码,写到一半停下来去打水,不希望别人动你的代码,你打水的时候决定把电脑锁屏。这是一个普遍的处理多个任务需要考虑的问题。
现代计算机已经从各个层面支持锁的实现,包括CPU的总线锁,缓存锁,汇编的Lock前缀等。总的来说,有多线程多进程的场景就需要用到锁来管理资源访问。
最简单的自旋锁的实现?
1 |
|
自旋锁在锁被持有时间很短的情况下效率较高,但如果持有时间较长,它可能会在等待锁时浪费大量的CPU资源,简单的说就是忙等。
在实际应用中,更复杂的锁机制(如互斥锁、读写锁等)会更常见,它们可能会结合使用原子操作、内核同步原语(如信号量、条件变量等)来实现。这些更高级的锁在锁不可用时会使线程休眠,直到锁可用时再唤醒线程,从而避免不必要的CPU资源浪费。
iOS中都有哪些锁可用?
在 iOS 开发中,有多种锁可用于同步访问共享资源,保证线程安全。不同类型的锁在性能和特性上各不相同。以下是一些常用的锁及其简要说明:
- NSLock:
NSLock
是一个基本的互斥锁,提供了简单的加锁和解锁功能。使用起来非常直接,但没有提供高级功能。
- NSRecursiveLock:
NSRecursiveLock
允许同一个线程多次获得相同的锁,而不会导致死锁。这对于递归函数或可以从多个途径调用的方法很有用。
- NSCondition:
NSCondition
是一个条件锁,允许线程在满足特定条件之前等待,或在条件发生变化时通知其他线程。
- NSConditionLock:
NSConditionLock
是NSCondition
的一个变体,它添加了与特定条件关联的锁定状态。
- @synchronized:
- 这是一个 Objective-C 语言特性,它可以锁定一个对象,以保护一段代码的执行。使用起来简单,但性能不是最优。
- pthread_mutex:
- 这是一个基于 POSIX 线程(pthreads)的低级互斥锁。它比
NSLock
提供更多的灵活性和配置选项。
- 这是一个基于 POSIX 线程(pthreads)的低级互斥锁。它比
- pthread_rwlock:
- 这是一个基于 pthreads 的读写锁,允许多个线程同时读取但只允许一个线程写入。
- dispatch_semaphore:
dispatch_semaphore
是 GCD(Grand Central Dispatch)提供的一个计数信号量,可以用来控制访问共享资源的线程数量。
- OSSpinLock (已被弃用):
OSSpinLock
曾经是 iOS 中最快的锁类型之一,但由于潜在的安全问题,它在 iOS 10 中被标记为弃用。
- os_unfair_lock:
os_unfair_lock
是替代OSSpinLock
的锁,它为等待锁的线程提供了一种更为公平的调度策略。
- dispatch_queue:
- 虽然不是传统意义上的锁,但串行
dispatch_queue
可以用来同步访问,保证代码块按顺序执行。
- 虽然不是传统意义上的锁,但串行
每种锁都有其适用场景和性能特点。例如,在不需要高性能的简单场景中,NSLock
或 @synchronized
可能已足够。在需要支持递归锁定的场景中,可以使用 NSRecursiveLock
。而在性能至关重要的环境中,os_unfair_lock
或 dispatch_semaphore
可能是更好的选择。
在选择锁时,开发者需要考虑锁的性能特性以及线程竞争的程度。一般而言,应该尽量避免使用锁来提高并发性能,例如通过设计无锁的数据结构或使用其他同步机制(如原子操作)。
1 |
|

死锁问题
- 进程 A 持有锁 L1 并等待锁 L2。
- 进程 B 持有锁 L2 并等待锁 L1。
饥饿问题
- 饥饿是指一个或多个线程无法获得所需的资源,从而无法继续执行的情况。在锁的上下文中,饥饿可能发生在某些线程尝试获取锁,但总是有其他线程抢先一步获取锁,导致这些线程无限期地等待。
- 解决饥饿的方法包括使用公平锁(fair locks),确保线程按照请求锁的顺序获得锁,或者调整线程的优先级和调度策略。
优先级反转
高优先级任务A尝试获取一个当前被低优先级任务B持有的锁。
任务A因为锁被占用而被迫等待(阻塞状态)。
此时,中优先级任务C就绪并开始执行。因为任务C的优先级高于任务B,所以它得到了CPU时间。
由于任务C在运行,低优先级任务B无法获得CPU执行时间来完成其工作并释放锁。
因此,尽管任务A有最高的优先级,它仍然被阻塞,因为它等待的锁被低优先级任务B持有,而任务B又无法运行来释放锁。
在这个场景中,中优先级任务C并没有直接参与锁的竞争,但它的运行阻碍了低优先级任务B的进度,从而间接导致了