目录
一,安全隐患
二,OSSpinLock
三,os_unfair_lock
四,pthread_mutex
五,NSLock
六,NSRecursiveLock
七,NSCondition
八,NSConditionLock
九,@synchronized
十,总结
十一,pthread_rwlock
一,安全隐患
1,实例
@interface ViewController ()
@property (nonatomic, assign) int money;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self saveAndDrawMoney];
}
- (void)saveAndDrawMoney {
self.money = 100;
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
for (int i = 0; i < 5; i++) {
[self saveMoney];
}
});
dispatch_async(queue, ^{
for (int i = 0; i < 5; i++) {
[self drawMoney];
}
});
}
- (void)saveMoney {
[self _saveMoney];
}
- (void)_saveMoney {
int oldMoney = self.money;
sleep(0.3);
oldMoney += 40;
self.money = oldMoney;
NSLog(@"存40元,余额%d元---%@", self.money, [NSThread currentThread]);
}
- (void)drawMoney {
[self _drawMoney];
}
- (void)_drawMoney {
int oldMoney = self.money;
sleep(0.3);
oldMoney -= 20;
self.money = oldMoney;
NSLog(@"取20元,余额%d元---%@", self.money, [NSThread currentThread]);
}
@end
// 打印(最后余额应该是200的)
取20元,余额80元---{number = 7, name = (null)}
存40元,余额140元---{number = 6, name = (null)}
取20元,余额60元---{number = 7, name = (null)}
存40元,余额100元---{number = 6, name = (null)}
取20元,余额80元---{number = 7, name = (null)}
存40元,余额120元---{number = 6, name = (null)}
取20元,余额100元---{number = 7, name = (null)}
存40元,余额140元---{number = 6, name = (null)}
取20元,余额120元---{number = 7, name = (null)}
存40元,余额160元---{number = 6, name = (null)}
2,图解
3,解决方案
使用线程同步技术让线程按顺序对同一资源进行访问
最常用的线程同步技术是对资源进行加锁
二,OSSpinLock
1,使用
#import
@interface ViewController ()
@property (nonatomic, assign) OSSpinLock lock;
@end
- (void)viewDidLoad {
[super viewDidLoad];
// 初始化锁
self.lock = OS_SPINLOCK_INIT;
[self saveAndDrawMoney];
}
- (void)saveMoney {
// 将_lock锁上
OSSpinLockLock(&_lock);
[self _saveMoney];
// 将_lock打开
OSSpinLockUnlock(&_lock);
}
- (void)drawMoney {
// 将_lock锁上
OSSpinLockLock(&_lock);
[self _drawMoney];
// 将_lock打开
OSSpinLockUnlock(&_lock);
}
// 打印
存40元,余额140元---{number = 5, name = (null)}
存40元,余额180元---{number = 5, name = (null)}
存40元,余额220元---{number = 5, name = (null)}
存40元,余额260元---{number = 5, name = (null)}
存40元,余额300元---{number = 5, name = (null)}
取20元,余额280元---{number = 4, name = (null)}
取20元,余额260元---{number = 4, name = (null)}
取20元,余额240元---{number = 4, name = (null)}
取20元,余额220元---{number = 4, name = (null)}
取20元,余额200元---{number = 4, name = (null)}
2,说明
当线程执行到
OSSpinLockLock
函数时,会判断_lock
是否已锁上,如果否就将它锁上并继续往下执行,如果是就等待它被打开等待有两种:忙等和休眠,忙等会一直占用着CPU资源,休眠则不会
等待
OSSpinLock
(自旋锁)的线程会处于忙等状态
3,不推荐使用
iOS10开始就被废弃了
不安全,可能会出现优先级反转问题
如果处于忙等状态的线程优先级较高,它会一直占用着CPU资源,优先级较低的线程就无法将锁打开,从而导致死锁
三,os_unfair_lock
1,使用
#import
@interface ViewController ()
@property (nonatomic, assign) os_unfair_lock lock;
@end
- (void)viewDidLoad {
[super viewDidLoad];
// 初始化锁
self.lock = OS_UNFAIR_LOCK_INIT;
[self saveAndDrawMoney];
}
- (void)saveMoney {
// 将_lock锁上
os_unfair_lock_lock(&_lock);
[self _saveMoney];
// 将_lock打开
os_unfair_lock_unlock(&_lock);
}
- (void)drawMoney {
// 将_lock锁上
os_unfair_lock_lock(&_lock);
[self _drawMoney];
// 将_lock打开
os_unfair_lock_unlock(&_lock);
}
2,说明
从iOS10开始支持,用于取代不安全的
OSSpinLock
等待
os_unfair_lock
的线程会处于休眠状态如果忘记将锁打开,其他线程会一直等待,从而导致死锁
四,pthread_mutex
1,normal
(互斥锁)
- 使用
#import
@interface ViewController ()
@property (nonatomic, assign) pthread_mutex_t mutex;
@end
- (void)viewDidLoad {
[super viewDidLoad];
// 初始化属性
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL); // normal
// 初始化锁
pthread_mutex_init(&_mutex, &attr);
// 销毁属性
pthread_mutexattr_destroy(&attr);
[self saveAndDrawMoney];
}
- (void)saveMoney {
// 将_mutex锁上
pthread_mutex_lock(&_mutex);
[self _saveMoney];
// 将_mutex打开
pthread_mutex_unlock(&_mutex);
}
- (void)drawMoney {
// 将_mutex锁上
pthread_mutex_lock(&_mutex);
[self _drawMoney];
// 将_mutex打开
pthread_mutex_unlock(&_mutex);
}
- (void)dealloc {
// 销毁锁
pthread_mutex_destroy(&_mutex);
}
- 说明
1>初始化锁时,属性可以传
NULL
2>等待
pthread_mutex
的线程会处于休眠状态
2,recursive
(递归锁)
- 使用
- (void)viewDidLoad {
[super viewDidLoad];
// 初始化属性
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); // recursive
// 初始化锁
pthread_mutex_init(&_mutex, &attr);
// 销毁属性
pthread_mutexattr_destroy(&attr);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self recursive];
});
}
- (void)recursive {
// 将_mutex锁上
pthread_mutex_lock(&_mutex);
static int count = 0;
count++;
if (count <= 5) {
NSLog(@"%s---%@", __func__, [NSThread currentThread]);
// 递归
[self recursive];
}
// 将_mutex打开
pthread_mutex_unlock(&_mutex);
}
// 打印
-[ViewController recursive]---{number = 5, name = (null)}
-[ViewController recursive]---{number = 5, name = (null)}
-[ViewController recursive]---{number = 5, name = (null)}
-[ViewController recursive]---{number = 5, name = (null)}
-[ViewController recursive]---{number = 5, name = (null)}
- 说明
1>如果递归调用的方法里有用到锁,那么就必须用递归锁,否则会出现死锁
2>同一个线程可以对递归锁进行重复上锁
3,pthread_cond
(条件锁)
- 使用
@interface ViewController ()
@property (nonatomic, assign) pthread_cond_t cond;
@property (nonatomic, strong) NSMutableArray *mArray;
@end
- (void)viewDidLoad {
[super viewDidLoad];
// 初始化锁
pthread_mutex_init(&_mutex, NULL);
// 初始化条件
pthread_cond_init(&_cond, NULL);
[self condition];
}
- (void)condition {
self.mArray = [NSMutableArray new];
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
sleep(1.0);
[self add];
});
dispatch_async(queue, ^{
[self remove];
});
}
- (void)add {
// 将_mutex锁上
pthread_mutex_lock(&_mutex);
[self.mArray addObject:@"1"];
NSLog(@"%s---%@", __func__, [NSThread currentThread]);
// 发送信号
pthread_cond_signal(&_cond);
// 将_mutex打开
pthread_mutex_unlock(&_mutex);
}
- (void)remove {
// 将_mutex锁上
pthread_mutex_lock(&_mutex);
if (self.mArray.count == 0) {
// 等待信号
pthread_cond_wait(&_cond, &_mutex);
}
[self.mArray removeLastObject];
NSLog(@"%s---%@", __func__, [NSThread currentThread]);
// 将_mutex打开
pthread_mutex_unlock(&_mutex);
}
- (void)dealloc {
// 销毁锁
pthread_mutex_destroy(&_mutex);
// 销毁条件
pthread_cond_destroy(&_cond);
}
// 打印
-[ViewController add]---{number = 5, name = (null)}
-[ViewController remove]---{number = 7, name = (null)}
- 说明
1>某个线程中的操作在某种情况下需要依赖另外一个线程中的操作,那么就可以用条件来实现
2>
pthread_cond_wait
函数会让线程进入休眠状态,并且将锁打开3>当收到信号时,
pthread_cond_wait
函数会唤醒线程,并且将锁再次锁上
五,NSLock
它是对pthread_mutex(normal)
的封装
@interface ViewController ()
@property (nonatomic, strong) NSLock *lock;
@end
- (void)viewDidLoad {
[super viewDidLoad];
// 初始化锁
self.lock = [NSLock new];
[self saveAndDrawMoney];
}
- (void)saveMoney {
// 将lock锁上
[self.lock lock];
[self _saveMoney];
// 将lock打开
[self.lock unlock];
}
- (void)drawMoney {
// 将lock锁上
[self.lock lock];
[self _drawMoney];
// 将lock打开
[self.lock unlock];
}
六,NSRecursiveLock
它是对pthread_mutex(recursive)
的封装
@interface ViewController ()
@property (nonatomic, strong) NSRecursiveLock *lock;
@end
- (void)viewDidLoad {
[super viewDidLoad];
// 初始化锁
self.lock = [NSRecursiveLock new];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self recursive];
});
}
- (void)recursive {
// 将lock锁上
[self.lock lock];
static int count = 0;
count++;
if (count <= 5) {
NSLog(@"%s---%@", __func__, [NSThread currentThread]);
// 递归
[self recursive];
}
// 将lock打开
[self.lock unlock];
}
七,NSCondition
它是对pthread_mutex
和pthread_cond
的封装
@interface ViewController ()
@property (nonatomic, strong) NSCondition *lock;
@end
- (void)viewDidLoad {
[super viewDidLoad];
// 初始化锁
self.lock = [NSCondition new];
[self condition];
}
- (void)add {
// 将lock锁上
[self.lock lock];
[self.mArray addObject:@"1"];
NSLog(@"%s---%@", __func__, [NSThread currentThread]);
// 发送信号
[self.lock signal];
// 将lock打开
[self.lock unlock];
}
- (void)remove {
// 将lock锁上
[self.lock lock];
if (self.mArray.count == 0) {
// 等待信号
[self.lock wait];
}
[self.mArray removeLastObject];
NSLog(@"%s---%@", __func__, [NSThread currentThread]);
// 将lock打开
[self.lock unlock];
}
八,NSConditionLock
它是对NSCondition
的封装,可以利用条件值来控制线程的执行顺序
@interface ViewController ()
@property (nonatomic, strong) NSConditionLock *lock;
@end
- (void)viewDidLoad {
[super viewDidLoad];
// 条件值初始化为1
self.lock = [[NSConditionLock alloc] initWithCondition:1];
[self condition];
}
- (void)condition {
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
[self one];
});
dispatch_async(queue, ^{
[self two];
});
dispatch_async(queue, ^{
[self three];
});
}
- (void)one {
// 条件值为3往下执行,否则等待
[self.lock lockWhenCondition:3];
NSLog(@"%s---%@", __func__, [NSThread currentThread]);
// 不设置条件值
[self.lock unlock];
NSLog(@"当前条件值---%zd", self.lock.condition);
}
- (void)two {
// 条件值为2往下执行,否则等待
[self.lock lockWhenCondition:2];
NSLog(@"%s---%@", __func__, [NSThread currentThread]);
// 条件值设置为3
[self.lock unlockWithCondition:3];
NSLog(@"当前条件值---%zd", self.lock.condition);
}
- (void)three {
// 条件值为1往下执行,否则等待
[self.lock lockWhenCondition:1];
NSLog(@"%s---%@", __func__, [NSThread currentThread]);
// 条件值设置为2
[self.lock unlockWithCondition:2];
NSLog(@"当前条件值---%zd", self.lock.condition);
}
// 打印
-[ViewController three]---{number = 4, name = (null)}
当前条件值---2
-[ViewController two]---{number = 6, name = (null)}
当前条件值---3
-[ViewController one]---{number = 7, name = (null)}
当前条件值---3
九,@synchronized
1,实例代码
- (void)viewDidLoad {
[super viewDidLoad];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self recursive];
});
}
- (void)recursive {
@synchronized (self) { // 调用objc_sync_enter函数
static int count = 0;
count++;
if (count <= 5) {
NSLog(@"%s---%@", __func__, [NSThread currentThread]);
[self recursive];
}
} // 调用objc_sync_exit函数
}
// 打印
-[ViewController recursive]---{number = 7, name = (null)}
-[ViewController recursive]---{number = 7, name = (null)}
-[ViewController recursive]---{number = 7, name = (null)}
-[ViewController recursive]---{number = 7, name = (null)}
-[ViewController recursive]---{number = 7, name = (null)}
2,底层代码(源码下载地址)
objc_sync_enter
int objc_sync_enter(id obj)
{
int result = OBJC_SYNC_SUCCESS;
if (obj) {
// 用obj生成一个锁
SyncData* data = id2data(obj, ACQUIRE);
assert(data);
// 将锁锁上
data->mutex.lock();
} else {
// @synchronized(nil) does nothing
if (DebugNilSync) {
_objc_inform("NIL SYNC DEBUG: @synchronized(nil); set a breakpoint on objc_sync_nil to debug");
}
objc_sync_nil();
}
return result;
}
typedef struct alignas(CacheLineSize) SyncData {
struct SyncData* nextData;
DisguisedPtr object;
int32_t threadCount; // number of THREADS using this block
// 递归锁
recursive_mutex_t mutex;
} SyncData;
objc_sync_exit
int objc_sync_exit(id obj)
{
int result = OBJC_SYNC_SUCCESS;
if (obj) {
// 用obj获取之前生成的锁
SyncData* data = id2data(obj, RELEASE);
if (!data) {
result = OBJC_SYNC_NOT_OWNING_THREAD_ERROR;
} else {
// 将锁打开
bool okay = data->mutex.tryUnlock();
if (!okay) {
result = OBJC_SYNC_NOT_OWNING_THREAD_ERROR;
}
}
} else {
// @synchronized(nil) does nothing
}
return result;
}
十,总结
1,关系图
2,性能图
⚠️注意⚠️:因为休眠和唤醒比忙等更加消耗CPU资源,所以互斥锁比自旋锁性能低
十一,pthread_rwlock
1,说明
- 多读单写
1>同一时间,允许有多个线程进行读的操作
2>同一时间,只能有一个线程进行写的操作
3>同一时间,不允许既有写的操作,又有读的操作
pthread_rwlock
是读写锁,专用于“多读单写”的场景等待
pthread_rwlock
的线程会处于休眠状态
2,使用
@interface ViewController ()
@property (nonatomic, assign) pthread_rwlock_t lock;
@end
- (void)viewDidLoad {
[super viewDidLoad];
// 初始化锁
pthread_rwlock_init(&_lock, NULL);
[self readAndWrite];
}
- (void)readAndWrite {
for (int i = 0; i < 5; i++) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self read];
[self write];
});
}
}
- (void)read {
// 将_lock锁上(读)
pthread_rwlock_rdlock(&_lock);
NSLog(@"%s---%@", __func__, [NSThread currentThread]);
sleep(1.0);
// 将_lock打开
pthread_rwlock_unlock(&_lock);
}
- (void)write {
// 将_lock锁上(写)
pthread_rwlock_wrlock(&_lock);
NSLog(@"%s---%@", __func__, [NSThread currentThread]);
sleep(1.0);
// 将_lock打开
pthread_rwlock_unlock(&_lock);
}
- (void)dealloc {
// 销毁锁
pthread_rwlock_destroy(&_lock);
}
// 打印(read同时执行,write顺序执行,read和write没有同时执行)
22:42:04.029314+0800 Demo[68231:11513342] -[ViewController read]---{number = 5, name = (null)}
22:42:04.029350+0800 Demo[68231:11513347] -[ViewController read]---{number = 3, name = (null)}
22:42:04.029384+0800 Demo[68231:11513340] -[ViewController read]---{number = 6, name = (null)}
22:42:04.029378+0800 Demo[68231:11513341] -[ViewController read]---{number = 4, name = (null)}
22:42:04.029697+0800 Demo[68231:11513354] -[ViewController read]---{number = 7, name = (null)}
22:42:05.034127+0800 Demo[68231:11513342] -[ViewController write]---{number = 5, name = (null)}
22:42:06.039197+0800 Demo[68231:11513340] -[ViewController write]---{number = 6, name = (null)}
22:42:07.043600+0800 Demo[68231:11513347] -[ViewController write]---{number = 3, name = (null)}
22:42:08.044081+0800 Demo[68231:11513341] -[ViewController write]---{number = 4, name = (null)}
22:42:09.049382+0800 Demo[68231:11513354] -[ViewController write]---{number = 7, name = (null)}