iOS 常见锁及其底层实现

一、OSSpinLock(已废弃⚠️)

头文件: <libkern/OSAtomic.h>

底层: 原子[自旋锁(CPU 自旋 + LDR / STR 指令)](#自旋锁(CPU 自旋 + LDR / STR 指令) "#102")

objc 复制代码
#import <libkern/OSAtomic.h>
OSSpinLock lock = OS_SPINLOCK_INIT;
// 加锁
OSSpinLockLock(&lock);
// 解锁
OSSpinLockUnlock(&lock);

底层实现:

objc 复制代码
Lspinlock:
    LDREX  R1, [R0]  ; 加载锁的值到 R1
    CMP    R1, #0    ; 如果锁是 0(未加锁)
    STREX  R2, R3, [R0] ; 尝试设置为 1(加锁)
    CMP    R2, #0
    BNE    Lspinlock  ; 如果失败,继续自旋

✅ 优点:性能极高(无 syscall 进入内核)。

❌ 缺点:容易造成"优先级反转"(高优先级线程被低优先级线程饿死)。

❌ iOS 10+ 已被 os_unfair_lock 取代。

二、os_unfair_lock

objc 复制代码
#import <os/lock.h>
os_unfair_lock lock = OS_UNFAIR_LOCK_INIT;

os_unfair_lock_lock(&lock);   // 加锁
os_unfair_lock_unlock(&lock); // 解锁

🔹 底层实现

objc 复制代码
Llock:
    LDREX  R1, [R0]  ; 加载锁值
    CMP    R1, #0    ; 检查是否可用
    STREX  R2, R3, [R0] ; 尝试加锁
    CMP    R2, #0
    BNE    Llock  ; 如果失败,自旋等待

    ; 如果锁被占用,则进入内核等待
    BL     _os_unfair_lock_wait

os_unfair_lock 先尝试自旋

• 如果竞争激烈,会进入 syscall 休眠

• 避免 CPU 过度消耗,提高系统吞吐量

✅ 优点:比 OSSpinLock 更安全(不会优先级反转)。

✅ 推荐替代 OSSpinLock,用于多线程同步。

三、pthread_mutex(互斥锁)

objc 复制代码
#import <pthread.h>
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

// 加锁
pthread_mutex_lock(&mutex);

// 解锁
pthread_mutex_unlock(&mutex);

🔹 底层实现

assembly 复制代码
Lmutex:
    LDREX  R1, [R0]  ; 读取锁状态
    CMP    R1, #0
    STREX  R2, R3, [R0] ; 尝试锁定
    BNE    Lmutex  ; 失败则等待
    BL     _futex_wait  ; 进入系统调用

• 第一次尝试自旋(适用于短时间锁定)

• 如果锁不可用,进入 futex_wait 系统调用

• 性能比 NSLock

✅ 优点:比 NSLock 快,适用于 C 代码。

✅ 适用于 POSIX 兼容的锁定需求。

四、NSLock

objc 复制代码
NSLock *lock = [[NSLock alloc] init];
[lock lock];   // 加锁
[lock unlock]; // 解锁

🔹 底层

• 封装 pthread_mutexpthread_mutex 稍慢

• 适用于 Objective-C 代码

✅ 优点:Objective-C 友好,适用于 Foundation 框架。

五、dispatch_semaphore(信号量)

objc 复制代码
dispatch_semaphore_t sema = dispatch_semaphore_create(1);

// 线程 1
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);

// 线程 2
dispatch_semaphore_signal(sema);

🔹 底层

• 使用 semaphore_wait() 进入 mach_syscall

• 基于 Mach 机制的 semaphore,比 NSLock 更轻量

• 可用于线程池、资源控制、线程同步、线程池控制

六、NSRecursiveLock

✅ 递归 pthread_mutex,递归锁,比NSLock慢。

允许同一线程多次加锁。

七、dispatch_queue (serial)

串行队列,用于线程同步。

八、NSCondition

基于 pthread_cond,条件锁。

九、NSConditionLock

基于 NSCondition ,比NSCondition慢。

十、什么是原子(atomic)

原子表示操作不可被中断,即多个线程同时访问变量时,不会被读取到中间状态,保证数据一致性。

@property (atomic, strong) NSString *name;

编译器会自动生成:

objc 复制代码
- (void)setName:(NSString *)name {
    @synchronized(self) {  // 保护 name 赋值操作
        _name = name; // ARC
    }
}

atomic保证setName:线程安全。

atomic不能保证多个操作的整体原子性(比如 name.length + name.uppercaseString 仍然可能导致数据竞争)。

✅ 原子操作的底层实现

assembly 复制代码
Latomic_add:
    LDREX  R1, [R0]  ; 读取 _name
    ADD    R1, R1, #1 ; R1 = R1 + 1
    STREX  R2, R1, [R0] ; 尝试写回 _name
    CMP    R2, #0
    BNE    Latomic_add  ; 如果失败,重试

• LDREX(Load Exclusive)读取变量。

• STREX(Store Exclusive)写入变量,并检查是否有其他线程修改过它。

• 如果 STREX 失败,重新执行(自旋),直到成功。

适用于:

• 多线程数据一致性

• 需要保证单个属性的安全性

• 高并发环境

十一、什么是非原子(nonatomic)

非原子表示操作可能被中断,即多个线程同时访问变量时,可能读取到不完整的数据。

比如:

@property (nonatomic, strong) NSString *name;

编译器不会自动加锁:

objc 复制代码
- (void)setName:(NSString *)name {
    _name = name; // ARC
}

nonatomic 不保证setName:线程安全。

nonatomic 性能更高,因为不需要锁定和等待。

比如:

objc 复制代码
self.name = @"Hello";
dispatch_async(queue, ^{
    self.name = @"World";
});
NSLog(@"%@", self.name);  // 可能是 "Hello",可能是 "World",也可能是崩溃

多个线程访问name,可能导致数据竞争。

适用于:

• 单线程环境

• 不涉及数据竞争的情况

• 性能优先的情况

十二、什么是自旋(Spinlock)

自旋(Spinlock)一种高性能的锁 ,线程 不会立即进入休眠 ,而是 持续循环检查锁是否可用(自旋等待)。

objc 复制代码
#import <libkern/OSAtomic.h>
OSSpinLock lock = OS_SPINLOCK_INIT;

OSSpinLockLock(&lock);   // 自旋等待
// 线程安全的操作
OSSpinLockUnlock(&lock); // 释放锁

✅ 自旋锁的核心特点

• 线程不会立即挂起,而是死循环检查锁状态

• 如果锁很快被释放(低竞争),自旋锁更快

• 如果锁被长时间持有(高竞争),自旋锁会浪费 CPU 资源

• 适用于短时间锁定的场景

容易造成优先级反转,iOS 10 之后,Apple 已废弃 OSSpinLock,使用 os_unfair_lock 代替 OSSpinlock

🙋 什么是"优先级反转"?

🔹 假设有两个线程

T1(低优先级) 持有 OSSpinLock,但被 T2 抢占 CPU,无法执行

T2(高优先级) 需要获取 OSSpinLock,但 T1 迟迟不释放

结果:T2 只能一直等待,导致"高优先级线程被低优先级线程饿死"

objc 复制代码
#import <Foundation/Foundation.h>
#import <libkern/OSAtomic.h>

OSSpinLock spinlock = OS_SPINLOCK_INIT;

void lowPriorityTask() {
    NSLog(@"低优先级线程尝试加锁...");
    OSSpinLockLock(&spinlock);  // 加锁
    NSLog(@"低优先级线程已获取锁,执行任务...");
    
    sleep(5);  // 模拟任务执行时间(期间不释放锁)

    NSLog(@"低优先级线程即将释放锁...");
    OSSpinLockUnlock(&spinlock);  // 解锁
}

void highPriorityTask() {
    sleep(1); // 确保低优先级线程先运行

    NSLog(@"高优先级线程尝试加锁...");
    OSSpinLockLock(&spinlock);  // 这里会被阻塞

    NSLog(@"高优先级线程已获取锁,执行任务...");
    OSSpinLockUnlock(&spinlock);
}

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // 低优先级线程
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
            lowPriorityTask();
        });

        // 高优先级线程
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
            highPriorityTask();
        });

        [[NSRunLoop currentRunLoop] run]; // 让主线程保持运行
    }
    return 0;
}

🤔 为什么会出现优先级反转?

  1. OSSpinLock 是"自旋锁"

    T2(高优先级线程)不会进入睡眠,而是不断自旋,占用 CPU 资源,反而导致 T1(低优先级线程)更难执行完成。

  2. 低优先级线程可能被"抢占"

    如果系统有更高优先级的任务,T1(低优先级线程)可能会被"饿死" ,导致它迟迟不能释放锁。

  3. 高优先级线程无法主动"让步"

    T2 不会让出CPU资源,它的调度优先级更高,反而让 T1 更难执行完,形成死循环。

🙋 如何解决"优先级反转"

✅ 使用 os_unfair_lock 代替 OSSpinLock(推荐)

objc 复制代码
#import <os/lock.h>

os_unfair_lock unfairLock = OS_UNFAIR_LOCK_INIT;

void lowPriorityTask() {
    NSLog(@"低优先级线程尝试加锁...");
    os_unfair_lock_lock(&unfairLock);  // ✅ 替换 `OSSpinLockLock`

    NSLog(@"低优先级线程已获取锁,执行任务...");
    sleep(5);
    
    NSLog(@"低优先级线程即将释放锁...");
    os_unfair_lock_unlock(&unfairLock);  // ✅ 替换 `OSSpinLockUnlock`
}

• 如果 T2(高优先级线程)发现锁被占用,它会进入 syscall 休眠,让 T1 继续执行。

• 避免 T2 占用 CPU,导致 T1 不能释放锁,防止死锁。

✅ 使用 [pthread_mutex](#✅ 使用 pthread_mutex 代替 OSSpinLock "#3") 代替 [OSSpinLock](#✅ 使用 pthread_mutex 代替 OSSpinLock "#1")

objc 复制代码
#import <pthread.h>

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

void lowPriorityTask() {
    NSLog(@"低优先级线程尝试加锁...");
    pthread_mutex_lock(&mutex);  // ✅ 替换 `OSSpinLockLock`

    NSLog(@"低优先级线程已获取锁,执行任务...");
    sleep(5);

    NSLog(@"低优先级线程即将释放锁...");
    pthread_mutex_unlock(&mutex);  // ✅ 替换 `OSSpinLockUnlock`
}

pthread_mutex 是互斥锁,不会自旋,而是让线程进入睡眠等待,提高系统性能

• 当高优先级线程等待锁时,低优先级线程会继承高优先级,加快任务执行,减少等待时间。

相关推荐
namehu1 小时前
搞定 iOS App 测试包分发,也就这么简单!😎
前端·ios·app
用户095 小时前
如何避免写垃圾代码:iOS开发篇
ios·swiftui·swift
HarderCoder19 小时前
iOS 知识积累第一弹:从 struct 到 APP 生命周期的全景复盘
ios
叽哥1 天前
Flutter Riverpod上手指南
android·flutter·ios
用户092 天前
SwiftUI Charts 函数绘图完全指南
ios·swiftui·swift
YungFan2 天前
iOS26适配指南之UIColor
ios·swift
权咚3 天前
阿权的开发经验小集
git·ios·xcode
用户093 天前
TipKit与CloudKit同步完全指南
ios·swift
法的空间3 天前
Flutter JsonToDart 支持 JsonSchema
android·flutter·ios
2501_915918413 天前
iOS 上架全流程指南 iOS 应用发布步骤、App Store 上架流程、uni-app 打包上传 ipa 与审核实战经验分享
android·ios·小程序·uni-app·cocoa·iphone·webview