浅析iOS开发中的各种锁

OSSpinLock 自旋锁 ( heigh-leve Lock、自旋锁)

使用需导入头文件

#import <libkern/OSAtomic.h>

使用方式

OSSpinLock _lock = OS_SPINLOCK_INIT; // 初始化一个锁
OSSpinLockLock(&_lock);  // 上锁
OSSpinLockUnlock(&_lock);  // 解锁
OSSpinLockTry(&_lock); // 尝试加锁 如果不能加锁 返回No

存在问题

1.线程优先级反转
2.iOS10之后过期

自旋锁,线程不会休眠,所以当低优先级线程先对操作进行Lock操作后,CPU调度高优先级线程操作,由于低优先级别UnLock,此时调用高优先级线程。高优先级无法处理该操作,而高优先级线程一直调用CPU资源, 系统等待高优先级线程执行完毕后才给低优先级线程资源。

os_unfair_lock (low-level Lock、互斥锁 )

使用需导入头文件

import <os.lock.h>

使用方式

os_unfair_lock _lock = OS_UNFAIR_LOCK_INIT;  // 初始化锁
os_unfair_lock_lock(&_lock);  // 上锁 
os_unfair_lock_unlock(&_lock); // 解锁
os_unfair_lock_trylock(&_lock); // 尝试加锁 如果不能加锁 返回No复

ptheard_mutex

使用需导入头文件

#import "pthread.h"

使用方式

1.属性初始化

// 初始化属性
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
// 设置锁的描述  
// PTHREAD_MUTEX_DEFAULT为一般互斥锁  
// PTHREAD_MUTEX_RECURSIVE为递归锁
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_DEFAULT); // 普通互斥锁
// 初始化锁
pthread_mutex_t mutex;
pthread_mutex_init(&mutex, &attr);
// 销毁属性
pthread_mutexattr_destroy(&attr);
// 加锁
pthread_mutex_lock(&mutex);
// 解锁
pthread_mutex_unlock(&mutex);

2.静态初始化

// 静态初始化 
// 属性为 PTHREAD_MUTEX_DEFAULT 
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

3.无属性初始化

// 初始化锁
// 属性为 PTHREAD_MUTEX_DEFAULT 
pthread_mutex_t mutex;
pthread_mutex_init(mutex, NULL);

4.递归锁说明

// 允许同一线程重复加锁
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);

5.拓展使用

// 注意 pthread_cond_wait 和 pthread_cond_signal 不可在统一线程被调用
// 原因是 pthread_cond_wait 会使线程休眠

// 初始化条件
pthread_cond_t cond
pthread_cond_init(&cond, NULL);

// 等待信号 同时解锁mutex
pthread_cond_wait(&cond, &mutex);

// 发送信号 继续进行 pthread_cond_wait 后续逻辑
pthread_cond_signal(&cond);

NSLock & NSRecursiveLock & NSCondition & NSConditionLock

可以查看源码 NSLock.swift
也可以查看 GNUstep 通过汇编分析出来的代码实现。链接地址
发现其内部封装的 ptheard_mutex 本初就不做多介绍了。

Dispatch_semaphore

Dispatch_semaphore 可以控制线程的个数 当控制线程个数为1的时候 能确保同时只有1条线程去访问,已达到确保线程安全的目的

使用方式

// 设置最大控制线程数量 number
dispatch_semaphore_t semaphore = dispatch_semaphore_create(number);

// 如果信号量<=0 当前线程就会进入休眠状态 直到信号量的值>0
// 如果信号值>0 就-1 然后执行后续代码
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);

// do something

// 信号量+1
dispatch_semaphore_signal(semaphore);

dispatch_queue(DISPATCH_QUEUE_SERIAL)

串行队列,当在一个串行队列执行的时候,只有一个线程,能确保线程安全

@synchronized

本质上也是对ptheard_mutex的封装.可以去runtime源码中objc-sync.mm文件查看。

Atomic

如果成员变量声明成为atomic的话。会在setget方法中添加锁。
源码可以查看runtime中的objc-accessors.mm文件

get 函数

id objc_getProperty(id self, SEL _cmd, ptrdiff_t offset, BOOL atomic) {
    if (offset == 0) {
        return object_getClass(self);
    }

    // Retain release world
    id *slot = (id*) ((char*)self + offset);
    // 如果不加锁 直接返回值
    if (!atomic) return *slot; 

    // 内部封装的 os_unfair_lock 锁
    // Atomic retain release world
    spinlock_t& slotlock = PropertyLocks[slot];
    slotlock.lock();
    id value = objc_retain(*slot);
    slotlock.unlock();

    // for performance, we (safely) issue the autorelease OUTSIDE of the spinlock.
    return objc_autoreleaseReturnValue(value);
}

set 函数

static inline void reallySetProperty(id self, SEL _cmd, id newValue, ptrdiff_t offset, bool atomic, bool copy, bool mutableCopy)
{
    if (offset == 0) {
        object_setClass(self, newValue);
        return;
    }

    id oldValue;
    id *slot = (id*) ((char*)self + offset);

    if (copy) {
        newValue = [newValue copyWithZone:nil];
    } else if (mutableCopy) {
        newValue = [newValue mutableCopyWithZone:nil];
    } else {
        if (*slot == newValue) return;
        newValue = objc_retain(newValue);
    }

    if (!atomic) {
        oldValue = *slot;
        *slot = newValue;
    } else {
        spinlock_t& slotlock = PropertyLocks[slot];
        slotlock.lock();
        oldValue = *slot;
        *slot = newValue;        
        slotlock.unlock();
    }

    objc_release(oldValue);
}