• 说下OperationQueue 和 GCD 的区别,和各自的优势
  • 线程安全的处理手段有哪些
  • OC你了解的锁有哪些,在你回答的基础上进行二次提问



iOS 中的线程同步方案

OSSpingLock
os_unfair_lock
pthread_mutex
dispatch_semaphore
dispatch_queue(DISPATCH_QUEUE_SERIAL)
NSLock
NSRecursiveLock
NSCondition
NSConditionLock
@synchronized

OSSpingLock

OSSpingLock 叫做“自旋锁”,等待锁的线程会处于忙等状态(busy-wait),一直占着CPU资源。
目前已经不再安全,可能会出现优先级翻转问题

如果等待锁的线程的优先级高,他会一直占着CPU资源,优先级低线程就无法释放锁
需要导入头文件 #import <libkern/OSAtomic.h>

//初始化
OSSpinLock lock = OS_SPINLOCK_INIT;
// 尝试加锁(如果需要等待就不加锁,直接返回false,如果不需要等待就加锁返回true)
bool  result = OSSpinLockTry(&lock);
// 加锁
OSSpinLockLock(&lock)
// 解锁
OSSpinLockUnlock(&lock)

os_unfair_lock

os_unfair_lock用于取代不安全的OSSpinLock,从iOS10开始才支持
从底层调用看,等待os_unfair_lock锁的线程会处于休眠状态,并非忙等
需要导入头文件 #import <os/lock.h>

// 初始化
os_unfair_lock lock = OS_UNFAIR_LOCK_INIT;
// 尝试加锁
os_unfair_lock_trylock(&lock);
// 加锁
os_unfair_lock_lock(&lock);
// 解锁
os_unfair_lock_unlock(&lock);

pthread_mutex

mutex叫做“互斥锁”,等待加锁的线层会处于休眠状态
徐亚导入头文件 #import <pthread.h>

// 初始化锁的属性
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr,PTHREAD_MUTEX_NORMAL);
// 初始化锁
pthread_mutex_t mutex;
pthread_mutex_init(&mutex,&attr);
//尝试加锁
pthread_mutex_trylock(&mutex);
// 加锁
pthread_mutex_lock(&mutex);
// 解锁
pthread_mutex_unlock(&mutex);
// 销毁相关资源
pthread_mutexattr_destroy(&attr);
pthread_mutex_destroy(&mutex);

/*
 * Mutex type attributes
 */
 #define PTHREAD_MUTEX_NORMAL   0
 #define PTHREAD_MUTEX_ERRORCHECX   1
 #define PTHREAD_MUTEX_RECURSIVE   2
 #define PTHREAD_MUTEX_DEFAULT   PTHREAD_MUTEX_NORMAL

pthread_mutex - 递归锁

// 初始化锁的属性
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr,PTHREAD_MUTEX_RECURSIVE);
// 初始化锁
pthread_mutex_t mutex;
pthread_mutex_init(&mutex,&attr);

自旋锁、互斥锁的比较

什么情况下使用自旋锁比较划算

预计线程等待锁的时间很短
加锁的代码(临界区)经常被调用,但竞争情况很少发生
CPU资源不紧张
多核处理器

什么情况下使用互斥锁比较划算

预计线程等待时间较长
单核处理器
临界区有IO操作
临界区代码复杂或者循环量大
临界区竞争非常激烈

atomic

atomic用于保证属性setter、getter的原子性能操作,相当于在getter和setter内部加了线程同步的锁
可以参考源码objc4的objc-accessors.mm
它并不能保证使用属性的过程中是线程安全的。

iOS中的读写安全方案

思考如何实现如下场景

同一时间,只能有一个线程进行写的操作
同一时间,允许多个线程进行读写操作
同一时间,不允许既有写的操作,还有读的操作

上面的场景就是典型的“多读单写”,经常用于文件等数据的读写操作,iOS中的实现方案有

pthread_rwlock: 读写锁
dispatch_barrier_async:异步栅栏调用