多线程在提高了程序执行效率的同时,也伴随着一些隐患,其中最大的隐患发生在多个线程同时读写同一块资源时导致数据混乱。在iOS中,有以下几种方式可以解决此隐患
- OSSpinLock
- os-unfair-lock
- pthread-mutex
- NSLock
- NSRecursiveLock
- NSCondition
- NSConditionLock
- dispatch_queue
- dispatch_semaphore
- @synchronized
以下是对每种方案的详细介绍
OSSpinLock
- 概念
- OSSpinLock叫做“自旋锁”,等待锁的线程会处于忙等(busy-wait)状态
- 使用
-
需要导入头文件
#import <libkern/OSAtomic>
-
创建锁:
self.lockForMoney = OS_SPINLOCK_INIT;
-
加锁
OSSpinLockLock(&_lockForMoney);
-
解锁:
OSSpinLockUnlock(&_lockForMoney);
-
- 注意点
- 与互斥锁相比,自旋锁是一种效率很高的锁。
- 要解释这一点,首先要理解互斥锁和自旋锁的区别:如果是互斥锁,那么等待锁的线程会处于休眠状态,当锁打开的时候,等待的线程会被唤醒;而如果是自旋锁,那么等待锁的线程会处于忙等状态,只要锁一打开,等待锁的线程就会立马访问资源并且加锁。由于线程的休眠与唤醒也需要消耗资源,有时让线程稍微等下就可以了,没必要使线程进入休眠状态
- 在iOS10中OSSpinLock已被废弃;在iOS10及之后,苹果建议使用os-unfair-lock
- 废弃原因:自旋锁目前已经不安全,容易导致线程优先级的反转。如果等待锁的线程的优先级较高,它会一直占用CPU资源,等级较低的线程可能就无法释放锁,造成死锁。
- 与互斥锁相比,自旋锁是一种效率很高的锁。
os-unfair-lock
- 概念
- os-unfair-lock用于取代不安全的OSSpinLock,从iOS10才开始支持。
- 从底层调用看(下面会从汇编角度来分析自旋锁和互斥锁),等待os-unfair-lock锁的线程会处于休眠状态,而并非忙等,所以说虽然os-unfair-lock用于取代OSSpinLock,但是os-unfair-lock并不是自旋锁
- 使用
-
需要导入头文件
#import <os/lock.h>
-
初始化锁:
_moneyLock = OS_UNFAIR_LOCK_INIT;
-
加锁:
os_unfair_lock_lock(&_moneyLock);
-
解锁:
os_unfair_lock_unlock(&_moneyLock);
-
pthread_mutex
-
pthread开头,表示可以跨平台
-
用法
mutex.jpg -
递归锁:允许同一个线程对同一把锁进行重复加锁
// 初始化时设置成递归锁 pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); // 递归调用 - (void)otherTest { pthread_mutex_lock(&_mutex); NSLog(@"%s",__FUNCTION__); static int count = 0; count++; if (count < 5) { [self otherTest]; } pthread_mutex_unlock(&_mutex); }
-
带条件的pthread_mutex锁
pthread_mutex条件.png
从汇编角度来区分自旋锁和互斥锁
-
自旋锁,High-Level Lock ,简称HLL。从汇编角度来看,线程发现资源被加锁后,当前线程会一直执行一个循环,直到锁被放开。
123.png -
互斥锁,Low-level Lock,简称LLL。从汇编角度看,线程发现加锁后,当前线程会调用syscall,然后什么指令都不执行。
456.png
网友评论