赞
踩
资源共享
多个线程可能会访问同一块资源
当多个线程访问同一块资源时,很容易引发数据错乱
和数据安全
问题
假设总共15
张票,使用3
个异步线程并发执行,每个线程卖5
张票,观察最后的打印结果
@interface ViewController () @property (nonatomic, assign) int ticketsCount; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view. } - (void)saleTicket { int oldTicketsCount = self.ticketsCount; sleep(0.2); oldTicketsCount--; self.ticketsCount = oldTicketsCount; NSLog(@"还剩 %d 张票", oldTicketsCount); } - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { self.ticketsCount = 15; dispatch_queue_t queue = dispatch_get_global_queue(0, 0); dispatch_async(queue, ^{ for (int i = 0; i < 5; i++) { [self saleTicket]; } }); dispatch_async(queue, ^{ for (int i = 0; i < 5; i++) { [self saleTicket]; } }); dispatch_async(queue, ^{ for (int i = 0; i < 5; i++) { [self saleTicket]; } }); } @end
出现票重复卖票,最后没卖完的情况
时序图示意:
分析图:
线程同步
技术(同步,就是协同步调,按预定的先后次序进行)加锁
为了方便调试,我们做个简单封装,将需要加锁的代码放在ZSXBaseDemo
中,同时暴漏方法给子类重写:
每一把锁将创建一个ZSXBaseDemo
子类,然后重写上述方法,实现各自的加锁方式
ZSXBaseDemo.h
@interface ZSXBaseDemo : NSObject
- (void)moneyTest;
- (void)ticketTest;
- (void)otherTest;
#pragma mark - 暴露给子类去使用
- (void)__saveMoney;
- (void)__drawMoney;
- (void)__saleTicket;
@end
ZSXBaseDemo.m
@interface ZSXBaseDemo() @property (assign, nonatomic) int money; @property (assign, nonatomic) int ticketsCount; @end @implementation ZSXBaseDemo - (void)otherTest {} /** 存钱、取钱演示 */ - (void)moneyTest { self.money = 100; dispatch_queue_t queue = dispatch_get_global_queue(0, 0); dispatch_async(queue, ^{ for (int i = 0; i < 10; i++) { [self __saveMoney]; } }); dispatch_async(queue, ^{ for (int i = 0; i < 10; i++) { [self __drawMoney]; } }); } /** 存钱 */ - (void)__saveMoney { int oldMoney = self.money; sleep(.2); oldMoney += 50; self.money = oldMoney; NSLog(@"存50,还剩%d元 - %@", oldMoney, [NSThread currentThread]); } /** 取钱 */ - (void)__drawMoney { int oldMoney = self.money; sleep(.2); oldMoney -= 20; self.money = oldMoney; NSLog(@"取20,还剩%d元 - %@", oldMoney, [NSThread currentThread]); } /** 卖1张票 */ - (void)__saleTicket { int oldTicketsCount = self.ticketsCount; sleep(.2); oldTicketsCount--; self.ticketsCount = oldTicketsCount; NSLog(@"还剩%d张票 - %@", oldTicketsCount, [NSThread currentThread]); } /** 卖票演示 */ - (void)ticketTest { self.ticketsCount = 15; dispatch_queue_t queue = dispatch_get_global_queue(0, 0); // for (int i = 0; i < 10; i++) { // [[[NSThread alloc] initWithTarget:self selector:@selector(__saleTicket) object:nil] start]; // } dispatch_async(queue, ^{ for (int i = 0; i < 5; i++) { [self __saleTicket]; } }); dispatch_async(queue, ^{ for (int i = 0; i < 5; i++) { [self __saleTicket]; } }); dispatch_async(queue, ^{ for (int i = 0; i < 5; i++) { [self __saleTicket]; } }); } @end
"自旋锁"
,等待锁的线程会处于忙等
(busy-wait)状态,一直占用着CPU
资源不再安全
,可能会出现优先级反转
问题// 初始化
OSSpinLock lock = OS_SPINLOCK_INIT;
// 尝试加锁
bool result = OSSpinLockTry(&lock);
// 加锁
OSSpinLockLock(&lock);
// 解锁
OSSpinLockUnlock(&lock);
#import "OSSpinLockDemo.h" #import <libkern/OSAtomic.h> @interface OSSpinLockDemo () // High-level lock // 自旋锁,有优先级反转问题 @property (nonatomic, assign) OSSpinLock moneyLock; @property (nonatomic, assign) OSSpinLock ticketLock; @end @implementation OSSpinLockDemo - (instancetype)init { if (self = [super init]) { _moneyLock = OS_SPINLOCK_INIT; _ticketLock = OS_SPINLOCK_INIT; //使用方法 // 初始化 OSSpinLock lock = OS_SPINLOCK_INIT; // 尝试加锁 bool result = OSSpinLockTry(&lock); // 加锁 OSSpinLockLock(&lock); // 解锁 OSSpinLockUnlock(&lock); } return self; } #pragma mark reload - (void)__drawMoney { OSSpinLockLock(&_moneyLock); [super __drawMoney]; OSSpinLockUnlock(&_moneyLock); } - (void)__saveMoney { OSSpinLockLock(&_moneyLock); [super __saveMoney]; OSSpinLockUnlock(&_moneyLock); } - (void)__saleTicket { OSSpinLockLock(&_ticketLock); [super __saleTicket]; OSSpinLockUnlock(&_ticketLock); } #pragma mark end @end
对于
卖票
,只有__saleTicket
方法中需要使用锁
,因此也可以使用static
关键字创建静态变量,无需声明为属性
- (void)__saleTicket {
static OSSpinLock ticketLock = OS_SPINLOCK_INIT;
OSSpinLockLock(&ticketLock);
[super __saleTicket];
OSSpinLockUnlock(&ticketLock);
}
os_unfair_lock
用于取代不安全
的OSSpinLock
,从iOS10
开始才支持os_unfair_lock
锁的线程会处于休眠状态,并非忙等#import <os/lock.h>
// 初始化
os_unfair_lock lock = OS_UNFAIR_LOCK_INIT;
// 尝试加锁
bool result = os_unfair_lock_trylock(&lock);
// 加锁
os_unfair_lock_lock(&lock);
// 解锁
os_unfair_lock_unlock(&lock);
#import "OSUnfairLockDemo.h" #import <os/lock.h> @interface OSUnfairLockDemo() // Low-level lock // ll lock // lll // Low-level lock的特点等不到锁就休眠 @property (assign, nonatomic) os_unfair_lock moneyLock; @property (assign, nonatomic) os_unfair_lock ticketLock; @end @implementation OSUnfairLockDemo - (instancetype)init { if (self = [super init]) { _moneyLock = OS_UNFAIR_LOCK_INIT; _ticketLock = OS_UNFAIR_LOCK_INIT; //使用方法 // 初始化 os_unfair_lock lock = OS_UNFAIR_LOCK_INIT; // 尝试加锁 bool result = os_unfair_lock_trylock(&lock); // 加锁 os_unfair_lock_lock(&lock); // 解锁 os_unfair_lock_unlock(&lock); } return self; } #pragma mark reload - (void)__drawMoney { os_unfair_lock_lock(&_moneyLock); [super __drawMoney]; os_unfair_lock_unlock(&_moneyLock); } - (void)__saveMoney { os_unfair_lock_lock(&_moneyLock); [super __saveMoney]; os_unfair_lock_unlock(&_moneyLock); } - (void)__saleTicket { os_unfair_lock_lock(&_ticketLock); [super __saleTicket]; os_unfair_lock_unlock(&_ticketLock); } #pragma mark end @end
”互斥锁”
,等待锁的线程会处于休眠状态#import <pthread.h>
// 初始化锁 pthread_mutex_init(mutex, NULL); //相当于设置属性"PTHREAD_MUTEX_DEFAULT" //初始化属性并设置属性 pthread_mutexattr_t attr; pthread_mutexattr_init(&attr); pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_DEFAULT); //初始化锁 pthread_mutex_init(mutex, &attr); //加锁 pthread_mutex_lock(&_mutex); //尝试加锁 bool result = pthread_mutex_trylock(&_mutex); //解锁 pthread_mutex_unlock(&_mutex); //销毁相关资源 pthread_mutexattr_destroy(&attr); //销毁属性 pthread_mutex_destroy(&_mutex); //销毁锁
@interface MutexDemo () @property (assign, nonatomic) pthread_mutex_t ticketMutex; @property (assign, nonatomic) pthread_mutex_t moneyMutex; @end @implementation MutexDemo - (void)__initMutex:(pthread_mutex_t *)mutex { // 初始化锁 pthread_mutex_init(mutex, NULL); } - (instancetype)init { if (self = [super init]) { [self __initMutex:&_ticketMutex]; [self __initMutex:&_moneyMutex]; } return self; } #pragma mark reload - (void)__drawMoney { pthread_mutex_lock(&_moneyMutex); [super __drawMoney]; pthread_mutex_unlock(&_moneyMutex); } - (void)__saveMoney { pthread_mutex_lock(&_moneyMutex); [super __saveMoney]; pthread_mutex_unlock(&_moneyMutex); } - (void)__saleTicket { pthread_mutex_lock(&_ticketMutex); [super __saleTicket]; pthread_mutex_unlock(&_ticketMutex); } #pragma mark end - (void)dealloc { pthread_mutex_destroy(&_moneyMutex); pthread_mutex_destroy(&_ticketMutex); } @end
递归:允许
同一个线程
对一把锁
重复加锁
在otherTest
方法中,假设该方法中已经加锁,同时会调用另一个也需要加锁的方法
- (void)otherTest { pthread_mutex_lock(&_mutex); NSLog(@"%s", __func__); [self otherTest2]; pthread_mutex_unlock(&_mutex); } - (void)otherTest2 { pthread_mutex_lock(&_mutex); NSLog(@"%s", __func__); pthread_mutex_unlock(&_mutex); }
此时执行代码
代码只会执行到
[self otherTest2];
。此时出现死锁
,是因为otherTest
方法加锁后,调用otherTest2
,otherTest2
方法开始执行时也会加锁
,此时因为otherTest
方法还未解锁
,otherTest2
则进入等待解锁状态,而otherTest
需要等待otherTest2
方法执行完才继续,所以产生死锁
使用pthread_mutexattr_t
配置该锁为递归锁
:
PTHREAD_MUTEX_RECURSIVE
// 初始化锁
// pthread_mutex_init(mutex, NULL);
//初始化属性
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
//初始化锁
pthread_mutex_init(mutex, &attr);
//销毁属性
pthread_mutexattr_destroy(&attr);
此时可以正常执行完otherTest
、otherTest2
方法
如果otherTest
方法里面是递归调用otherTest
自身
- (void)otherTest {
pthread_mutex_lock(&_mutex);
NSLog(@"%s", __func__);
[self otherTest];
pthread_mutex_unlock(&_mutex);
}
这时候会死循环
调用otherTest
若增加一个计数,即可控制递归
调用的次数
- (void)otherTest {
pthread_mutex_lock(&_mutex);
NSLog(@"%s", __func__);
static int count = 0;
if (count < 5) {
count++;
[self otherTest];
}
pthread_mutex_unlock(&_mutex);
}
执行结果:
业务场景中,可能需要不同线程间的依赖关系
,比如线程1
需要等待线程2
执行完才能继续执行
假设有如下代码:
- (void)otherTest { [[[NSThread alloc] initWithTarget:self selector:@selector(__remove) object:nil] start]; [[[NSThread alloc] initWithTarget:self selector:@selector(__add) object:nil] start]; } - (void)__remove { NSLog(@"%s", __func__); pthread_mutex_lock(&_mutex); [self.data removeLastObject]; NSLog(@"删除一个元素"); pthread_mutex_unlock(&_mutex); } - (void)__add { NSLog(@"%s", __func__); pthread_mutex_lock(&_mutex); [self.data addObject:@"test"]; NSLog(@"添加一个元素"); pthread_mutex_unlock(&_mutex); }
使用多线程调用__remove
和__add
,两者执行顺序不确定。但是希望__remove
是在__add
之后执行,保证先加、再减。
这时候可以使用条件锁
来实现
// 初始化锁
pthread_t mutex;
pthread_mutex_init(&mutex, NULL);
// 初始化条件
pthread_cond_t condition;
pthread_cond_init(&condition, NULL);
// 等待条件(进入休眠,放开mutex 锁;被唤醒后,会再次对mutex加锁)
pthread_cond_wait(&condition, &mutex);
// 激活一个等待条件的线程
pthread_cond_signal(&condition);
// 激活所有等待条件的线程
pthread_cond_broadcast(&condition);
// 销毁资源
pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&condition);
#import "MutexDemo3.h" #import <pthread.h> @interface MutexDemo3 () @property (nonatomic, strong) NSMutableArray *data; @property (assign, nonatomic) pthread_mutex_t mutex; @property (nonatomic, assign) pthread_cond_t cond; @end @implementation MutexDemo3 - (instancetype)init { if (self = [super init]) { _data = [[NSMutableArray alloc] init]; //初始化属性 pthread_mutexattr_t attr; pthread_mutexattr_init(&attr); pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); //初始化锁 pthread_mutex_init(&_mutex, &attr); //初始化条件 pthread_cond_init(&_cond, NULL); //销毁属性 pthread_mutexattr_destroy(&attr); } return self; } - (void)otherTest { [[[NSThread alloc] initWithTarget:self selector:@selector(__remove) object:nil] start]; [[[NSThread alloc] initWithTarget:self selector:@selector(__add) object:nil] start]; } - (void)__remove { NSLog(@"%s", __func__); pthread_mutex_lock(&_mutex); if (self.data.count == 0) { //等待条件(进入休眠,放开mutex 锁;被唤醒后,会再次对mutex加锁) pthread_cond_wait(&_cond, &_mutex); } [self.data removeLastObject]; NSLog(@"删除一个元素"); pthread_mutex_unlock(&_mutex); } - (void)__add { NSLog(@"%s", __func__); pthread_mutex_lock(&_mutex); [self.data addObject:@"test"]; NSLog(@"添加一个元素"); //激活等待条件 pthread_cond_signal(&_cond); pthread_mutex_unlock(&_mutex); } - (void)dealloc { pthread_mutex_destroy(&_mutex); pthread_cond_destroy(&_cond); } @end
执行结果:
可以保证先添加,再删除
#import "NSLockDemo.h" @interface NSLockDemo() @property (strong, nonatomic) NSLock *ticketLock; @property (strong, nonatomic) NSLock *moneyLock; @end @implementation NSLockDemo - (instancetype)init { if (self = [super init]) { self.ticketLock = [[NSLock alloc] init]; self.moneyLock = [[NSLock alloc] init]; } return self; } - (void)__saleTicket { [self.ticketLock lock]; [super __saleTicket]; [self.ticketLock unlock]; } - (void)__saveMoney { [self.moneyLock lock]; [super __saveMoney]; [self.moneyLock unlock]; } - (void)__drawMoney { [self.moneyLock lock]; [super __drawMoney]; [self.moneyLock unlock]; } @end
#import "NSLockDemo2.h" @interface NSLockDemo2() @property (strong, nonatomic) NSRecursiveLock *recursiveLock; @end @implementation NSLockDemo2 - (instancetype)init { if (self = [super init]) { self.recursiveLock = [[NSRecursiveLock alloc] init]; } return self; } - (void)otherTest { [self.recursiveLock lock]; NSLog(@"%s", __func__); static int count = 0; if (count < 5) { count++; [self otherTest]; } [self.recursiveLock unlock]; } - (void)otherTest2 { [self.recursiveLock lock]; NSLog(@"%s", __func__); [self.recursiveLock unlock]; } @end
执行结果:
#import "NSConditionDemo.h" @interface NSConditionDemo() @property (strong, nonatomic) NSCondition *condition; @property (strong, nonatomic) NSMutableArray *data; @end @implementation NSConditionDemo - (instancetype)init { if (self = [super init]) { self.condition = [[NSCondition alloc] init]; self.data = [NSMutableArray array]; } return self; } - (void)otherTest { [[[NSThread alloc] initWithTarget:self selector:@selector(__remove) object:nil] start]; [[[NSThread alloc] initWithTarget:self selector:@selector(__add) object:nil] start]; } // 生产者-消费者模式 // 线程1 // 删除数组中的元素 - (void)__remove { [self.condition lock]; NSLog(@"__remove - begin"); if (self.data.count == 0) { // 等待 [self.condition wait]; } [self.data removeLastObject]; NSLog(@"删除了元素"); [self.condition unlock]; } // 线程2 // 往数组中添加元素 - (void)__add { [self.condition lock]; sleep(1); [self.data addObject:@"Test"]; NSLog(@"添加了元素"); // 信号 [self.condition signal]; // 广播 // [self.condition broadcast]; [self.condition unlock]; } @end
执行结果:
NSConditionLock
是对NSCondition
的进一步封装,可以设置具体的条件值#import "NSConditionLockDemo.h" @interface NSConditionLockDemo() @property (strong, nonatomic) NSConditionLock *conditionLock; @end @implementation NSConditionLockDemo - (instancetype)init { if (self = [super init]) { self.conditionLock = [[NSConditionLock alloc] initWithCondition:1]; } return self; } - (void)otherTest { [[[NSThread alloc] initWithTarget:self selector:@selector(__one) object:nil] start]; [[[NSThread alloc] initWithTarget:self selector:@selector(__two) object:nil] start]; [[[NSThread alloc] initWithTarget:self selector:@selector(__three) object:nil] start]; } - (void)__one { [self.conditionLock lockWhenCondition:1]; NSLog(@"%s", __func__); sleep(1); [self.conditionLock unlockWithCondition:2]; } - (void)__two { [self.conditionLock lockWhenCondition:2]; NSLog(@"%s", __func__); sleep(1); [self.conditionLock unlockWithCondition:3]; } - (void)__three { [self.conditionLock lockWhenCondition:3]; NSLog(@"%s", __func__); [self.conditionLock unlock]; } @end
执行结果:
通过设置Condition
,可以实现按想要的顺序执行
任务,或者说任务之间的依赖关系
condition
默认值是0
即:
使用[[NSConditionLock alloc] init]
初始化,condition
为 0
使用
- (void)lock
代表直接加锁
即:
[self.conditionLock lock]
GCD
的串行队列
,也是可以实现线程同步的#import "SerialQueueDemo.h" @interface SerialQueueDemo() @property (nonatomic, strong) dispatch_queue_t ticketQueue; @property (nonatomic, strong) dispatch_queue_t moneyQueue; @end @implementation SerialQueueDemo - (instancetype)init { if (self = [super init]) { self.ticketQueue = dispatch_queue_create("ticketQueue", DISPATCH_QUEUE_SERIAL); self.moneyQueue = dispatch_queue_create("moneyQueue", DISPATCH_QUEUE_SERIAL); } return self; } - (void)__saveMoney { dispatch_sync(self.moneyQueue, ^{ [super __saveMoney]; }); } - (void)__drawMoney { dispatch_sync(self.moneyQueue, ^{ [super __drawMoney]; }); } - (void)__saleTicket { dispatch_sync(self.ticketQueue, ^{ [super __saleTicket]; }); } @end
执行结果:
semaphore
叫做”信号量”
并发访问
的最大数量
1
,代表同时只允许1条线程
访问资源,保证线程同步
- (void)otherTest
方法循环创建20个线程执行- (void)test
方法,semaphore
初始值设置为5
#import "SemaphoreDemo.h" @interface SemaphoreDemo() @property (nonatomic, strong) dispatch_semaphore_t semaphore; @end @implementation SemaphoreDemo - (instancetype)init { if (self = [super init]) { self.semaphore = dispatch_semaphore_create(5); } return self; } - (void)otherTest { for (int i = 0; i < 20; i++) { [[[NSThread alloc] initWithTarget:self selector:@selector(test) object:nil] start]; } } - (void)test { dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER); sleep(2); NSLog(@"test - %@", [NSThread currentThread]); dispatch_semaphore_signal(self.semaphore); } @end
运行结果:
实现了控制最大并发数5
@interface SemaphoreDemo() @property (nonatomic, strong) dispatch_semaphore_t semaphore; @property (nonatomic, strong) dispatch_semaphore_t tacketSemaphore; @property (nonatomic, strong) dispatch_semaphore_t moneySemaphore; @end @implementation SemaphoreDemo - (instancetype)init { if (self = [super init]) { self.semaphore = dispatch_semaphore_create(5); self.tacketSemaphore = dispatch_semaphore_create(1); self.moneySemaphore = dispatch_semaphore_create(1); } return self; } - (void)otherTest { for (int i = 0; i < 20; i++) { [[[NSThread alloc] initWithTarget:self selector:@selector(test) object:nil] start]; } } - (void)test { dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER); sleep(2); NSLog(@"test - %@", [NSThread currentThread]); dispatch_semaphore_signal(self.semaphore); } - (void)__saveMoney { dispatch_semaphore_wait(self.moneySemaphore, DISPATCH_TIME_FOREVER); [super __saveMoney]; dispatch_semaphore_signal(self.moneySemaphore); } - (void)__drawMoney { dispatch_semaphore_wait(self.moneySemaphore, DISPATCH_TIME_FOREVER); [super __drawMoney]; dispatch_semaphore_signal(self.moneySemaphore); } - (void)__saleTicket { dispatch_semaphore_wait(self.tacketSemaphore, DISPATCH_TIME_FOREVER); [super __saleTicket]; dispatch_semaphore_signal(self.tacketSemaphore); } @end
执行结果:
虽然打印结果已经保证线程同步
,但是窗口收到了警告
QoS (Quality of Service),用来指示某任务或者队列的运行优先级
;
记录了持有者的api
都可以自动避免
优先级反转,系统会通过提高
相关线程的优先级来解决优先级反转的问题,如 dispatch_sync
, 如果系统不知道持有者所在的线程,则无法知道应该提高谁的优先级,也就无法解决反转问题。
慎用dispatch_semaphore
做线程同步
dispatch_semaphore
容易造成优先级反转,因为api没有
记录是哪个线程持有了信号量,所以有高优先级
的线程在等待锁的时候,内核无法知道该提高那个线程的优先级(QoS);
dispatch_semaphore
不能避免优先级反转的原因在调用dispatch_semaphore_wait()
的时候,系统不知道哪个线程会调用 dispatch_semaphore_signal()
方法,系统无法知道owner
信息,无法调整
优先级。dispatch_group
和semaphore
类似,在调用enter()
方法的时候,无法预知谁会leave()
,所以系统也不知道owner信息
@synchronized
是对mutex
递归锁的封装
@synchronized(obj)
内部会生成obj
对应的递归锁,然后进行加锁、解锁操作使用哈希表
结构,将穿进去的obj
作为key
,找到低层封装mutex
的锁,再进行加锁、解锁操作
@synchronized (obj):
obj
如果相同,则代表使用同一把锁
#import "SynchronizedDemo.h" @implementation SynchronizedDemo - (void)__saveMoney { // 取钱、存钱 共用一把锁 @synchronized (self) { [super __saveMoney]; } } - (void)__drawMoney { @synchronized (self) { [super __drawMoney]; } } - (void)__saleTicket { // 买票 - 单独创建一把锁 static NSObject *lock; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ lock = [[NSObject alloc] init]; }); @synchronized (lock) { [super __saleTicket]; } } @end
性能从高到低排序
性能排行仅供参考,不同环境实际效果可能不一样
@oubijiexi
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。