简单回顾
NSThread
NSThread的常见用法
- (void)创建及使用 {
// 创建
[NSThread detachNewThreadWithBlock:^{
[self doSomething];
}];
[NSThread detachNewThreadSelector:@selector(doSomething) toTarget:self withObject:nil];
NSThread *threadOne = [[NSThread alloc] initWithBlock:^{
[self doSomething];
}];
NSThread *threadTwo = [[NSThread alloc] initWithTarget:self selector:@selector(doSomething) object:nil];
// name
[threadTwo setName:@"threadTwo"];
// 程使用栈区大小,默认是512K
[threadTwo setStackSize:512];
// iOS8以后推荐使用qualityOfService属性,通过量化的优先级枚举值来设置
[threadTwo setQualityOfService:NSQualityOfServiceUserInteractive];
// 线程优先级设置 iOS8以前使用 给当前线程设定优先级,调度优先级的取值范围是0.0 ~ 1.0,默认0.5,值越大,优先级越高。
[threadTwo setThreadPriority:[NSThread mainThread].threadPriority]; //线程优先级设置 iOS8以前使用
// 状态
[threadTwo isExecuting];
[threadTwo isCancelled];
[threadTwo isFinished];
[threadTwo isMainThread];
//开启
[threadTwo start];
//取消线程并不会马上停止并退出线程,仅仅只作(线程是否需要退出)状态记录
[threadOne cancel];
// 当前线程是否子线程
[NSThread isMultiThreaded];
// 获取当前线程、主线程
[NSThread currentThread];
[NSThread mainThread];
[NSThread exit];//停止方法会立即终止除主线程以外所有线程(无论是否在执行任务)并退出,需要在掌控所有线程状态的情况下调用此方法,否则可能会导致内存问题。
// sleep
[NSThread sleepForTimeInterval:1.0];
[NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:1.0]];
// 给当前线程设定优先级,调度优先级的取值范围是0.0 ~ 1.0,默认0.5,值越大,优先级越高。
[NSThread setThreadPriority:1.0];
// 线程的调用都会有函数的调用函数的调用就会有栈返回地址的记录,在这里返回的是函 数调用返回的虚拟地址,说白了就是在该线程中函数调用的虚拟地址的数组
NSArray *addressArray = [NSThread callStackReturnAddresses];
// 同上面的方法一样,只不过返回的是该线程调用函数的名字数字
NSArray *nameArray = [NSThread callStackSymbols];
}
线程切换
- (void)切换线程 {
NSThread *thread = [[NSThread alloc]initWithTarget:self selector:@selector(延时操作) object:nil];
[thread setName:@"切换线程的thread"];
[thread start];
}
- (void)延时操作 {
NSLog(@"--%s-- IN thread:%@",__func__,[NSThread currentThread]);
[NSThread sleepForTimeInterval:2.0f];
[self performSelectorOnMainThread:@selector(主线程更新视图) withObject:nil waitUntilDone:YES];
}
- (void)主线程更新视图 {
NSLog(@"--%s-- IN thread:%@",__func__,[NSThread currentThread]);
}
-
NSQualityOfServiceUserInteractive:
最高优先级,用于用户交互事件 -
NSQualityOfServiceUserInitiated:
次高优先级,用于用户需要马上执行的事件 -
NSQualityOfServiceDefault:
默认优先级,主线程和没有设置优先级的线程都默认为这个优先级 -
NSQualityOfServiceUtility:
普通优先级,用于普通任务 -
NSQualityOfServiceBackground:
最低优先级,用于不重要的任务
NSOperation
常见用法
- (void)createInvocationOperation {
NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(doSomething) object:nil];
operation.completionBlock = ^{
NSLog(@"completionBlock --%s-- IN thread:%@",__func__,[NSThread currentThread]);
};
[operation start];
}
#pragma mark -
- (void)切换线程 {
NSInvocationOperation *operation = [[NSInvocationOperation alloc]initWithTarget:self
selector:@selector(延时操作)
object:nil];
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
[queue setName:@"OperationQueue"];
[queue setQualityOfService:NSQualityOfServiceUserInteractive];
[queue setMaxConcurrentOperationCount:5];
[queue addOperation:operation];
}
- (void)延时操作 {
NSLog(@"--%s-- IN thread:%@",__func__,[NSThread currentThread]);
[NSThread sleepForTimeInterval:2.0f];
NSInvocationOperation *operation = [[NSInvocationOperation alloc]initWithTarget:self
selector:@selector(主线程更新视图)
object:nil];
[[NSOperationQueue mainQueue] addOperation:operation];
}
- (void)主线程更新视图 {
NSLog(@"--%s-- IN thread:%@",__func__,[NSThread currentThread]);
}
#pragma mark - NSBlockOperation
- (void)createBlockOperation {
// 追加操作并发执行
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^(){
NSLog(@"执行第1次操作,线程:%@", [NSThread currentThread]);
}];
[operation addExecutionBlock:^() {
NSLog(@"又执行了1个新的操作,线程:%@", [NSThread currentThread]);
}];
[operation addExecutionBlock:^() {
NSLog(@"又执行了1个新的操作,线程:%@", [NSThread currentThread]);
}];
[operation addExecutionBlock:^() {
NSLog(@"又执行了1个新的操作,线程:%@", [NSThread currentThread]);
}];
// 开始执行任务
[operation start];
}
- (void)doSomething {
NSLog(@"doSomething IN thread:%@",[NSThread currentThread]);
}
添加任务依赖
- (void)创建依赖 {
//创建操作队列
NSOperationQueue *operationQueue=[[NSOperationQueue alloc]init];
//创建最后一个操作
NSBlockOperation *lastBlockOperation=[NSBlockOperation blockOperationWithBlock:^{
[NSThread sleepForTimeInterval:5.f];
NSLog(@"开始的任务");
}];
for (int i=0; i<5; ++i) {
//创建多线程操作
NSBlockOperation *blockOperation=[NSBlockOperation blockOperationWithBlock:^{
[NSThread sleepForTimeInterval:i];
NSLog(@"第%d个任务",i);
}];
//设置依赖操作为最后一个操作
[blockOperation addDependency:lastBlockOperation];
[operationQueue addOperation:blockOperation];
}
//将最后一个操作加入线程队列
[operationQueue addOperation:lastBlockOperation];
// log
// 开始的任务
// 第0个任务
// 第1个任务
// 第2个任务
// 第3个任务
// 第4个任务
}
任务执行状态控制(kvc)
isReady
isExecuting
isFinished
isCancelled
如果只重写了main,底层控制变更任务执行完成状态,以及任务退出
如果重写了start,自行控制任务状态
最大并发量 MaxConcurrentOperationCount
队列里可以加入很多个NSOperation, 可以把NSOperationQueue看作一个线程池,可往线程池中添加操作(NSOperation)到队列中。线程池中的线程可看作消费者,从队列中取走操作,并执行它。线程池中的线程数,也就是并发操作数。默认情况下是-1,-1表示没有限制,这样会同时运行队列中的全部的操作。
自定义 NSOperation
自定义非并发NSOperation
Methods to Override
For non-concurrent operations, you typically override only one method: main
Into this method, you place the code needed to perform the given task.
在官方文档中指出,非并发任务,直接将需要执行的任务放在main方法中,然后直接调用即可。 这样直接调用main方法会存在一个问题,由于没有实现finished属性,所以获取finished属性时,只会返回NO,而且任务加入到队列后,不会被删除,另外任务执行完后,回调也不会被执行,所以最好不要只实现一个main方法来使用。
- (void)main {
// 新建一个自动释放池,如果是异步执行操作,那么将无法访问到主线程的自动释放池
@autoreleasepool {
if (self.isCancelled) {
return;
}
// 耗时操作
if (self.isCancelled) {
return;
}
if (self.responseBlock) {
dispatch_async(dispatch_get_main_queue(), ^{
self.responseBlock();
});
}
}
}
自定义并发NSOperation
Methods to Override
If you are creating a concurrent operation, you need to override the following methods and properties at a minimum:
start
asynchronous
executing
finished
- 重写NSOperation的main方法,该方法会作为NSOperation所启动线程的执行体
在 operation 的 main 方法里面,必须提供 autorelease pool,因为你的 operation 完成后需要销毁 - 实现KVO机制,一旦你的 operation 开始了,必须通过 KVO,告诉所有的监听者,现在该operation的执行状态。(即修改isFinished,isExecuting的值)
- 重载 isExecuting方法和isFinished方法。在这两个方法中,必须返回一个线程安全值(通常是个BOOL值),这个值可以在 operation 中进行操作。
- 重写isAsynchronous(或者isConcurrent)
@interface FSConOperation (){
BOOL _isFinished;
BOOL _isExecuting;
}
@end
@implementation FSConOperation
- (instancetype)initWithUrl:(NSString *)url completion:(dispatch_block_t)completion {
if (self = [super init]) {
self.url = url;
self.responseBlock = completion;
}
return self;
}
// 必须重写这个主方法
- (void)main {
// 新建一个自动释放池,如果是异步执行操作,那么将无法访问到主线程的自动释放池
@autoreleasepool {
// 提供一个变量标识,来表示我们需要执行的操作是否完成了,当然,没开始执行之前,为NO
BOOL taskIsFinished = NO;
UIImage *image = nil;
// while 保证:只有当没有执行完成和没有被取消,才执行我们自定义的相应操作
while (taskIsFinished == NO && [self isCancelled] == NO){
// 获取图片数据
NSURL *url = [NSURL URLWithString:self.url];
NSData *imageData = [NSData dataWithContentsOfURL:url];
image = [UIImage imageWithData:imageData];
NSLog(@"Current Thread = %@", [NSThread currentThread]);
// 这里,设置我们相应的操作都已经完成,后面就是要通知KVO我们的操作完成了。
taskIsFinished = YES;
}
// KVO 生成通知,告诉其他线程,该operation 执行完了
[self willChangeValueForKey:@"isFinished"];
[self willChangeValueForKey:@"isExecuting"];
_isFinished = YES;
_isExecuting = NO;
[self didChangeValueForKey:@"isFinished"];
[self didChangeValueForKey:@"isExecuting"];
if (self.responseBlock) {
dispatch_async(dispatch_get_main_queue(), ^{
self.responseBlock();
});
}
}
}
- (void)start {
// 如果我们取消了在开始之前,我们就立即返回并生成所需的KVO通知
if ([self isCancelled]){
// 我们取消了该 operation,那么就要告诉KVO,该operation已经执行完成(isFinished)
// 这样,调用的队列(或者线程)会继续执行。
[self willChangeValueForKey:@"isFinished"];
_isFinished = NO;
[self didChangeValueForKey:@"isFinished"];
} else {
// 没有取消,那就要告诉KVO,该队列开始执行了(isExecuting)!那么,就会调用main方法,进行同步执行。
[self willChangeValueForKey:@"isExecuting"];
_isExecuting = YES;
[NSThread detachNewThreadSelector:@selector(main) toTarget:self withObject:nil];
[self didChangeValueForKey:@"isExecuting"];
}
}
- (BOOL)isExecuting {
return _isExecuting;
}
- (BOOL)isFinished {
return _isFinished;
}
// 与自定义同步NSOperation不同的是,必须要实现下面的方法
#if __IPHONE_OS_VERSION_MAX_ALLOWED < __IPHONE_7_0
- (BOOL)isConcurrent {
return YES;
}
#else
- (BOOL)isAsynchronous {
return YES;
}
#endif
@end
使用
FSConOperation *operation = [[FSConOperation alloc] initWithUrl:@"url" completion:^{
//do completion
}];
[operation start];
GCD
了解概念
dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);
dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
dispatch_queue_t queue = dispatch_queue_create("serial", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t queue = dispatch_queue_create("concurrent", DISPATCH_QUEUE_CONCURRENT);
有4个术语比较容易混淆:同步、异步、并发、串行
同步和异步主要影响:能不能开启新的线程
同步:在当前线程中执行任务,不具备开启新线程的能力
异步:在新的线程中执行任务,具备开启新线程的能力并发和串行主要影响:任务的执行方式
并发:多个任务并发(同时)执行
串行:一个(当前)任务执行完毕后,再执行下一个任务
操作方式 | 并发队列 | 串行队列(手动创建) | 串行队列(主队列) |
---|---|---|---|
同步(sync) | 没有开启新线程,串行执行 | 没有开启新线程,串行执行 | 没有开启新线程,串行执行 |
异步(async) | 有开启新线程,并发执行 | 有开启新线程,串行执行 | 没有开启新线程,串行执行 |
- 在当前串行队列中使用sync函数添加任务,会卡住当前的串行队列(产生死锁)。例如在viewDidLoad中,在主线程sync插入任务A。因为FIFO,主队列中需要viewDidLoad完成后才会执行任务A,但是因为sync任务A需要立即执行。而当前队列正在执行viewDidLoad。这样viewDidLoad等待任务A,任务A等待viewDidLoad,造成死锁
GCD应用
用 dispatch_async 处理后台任务
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ // 1
UIImage *overlayImage = [self faceOverlayImageFromImage:_image];
dispatch_async(dispatch_get_main_queue(), ^{ // 2
[self fadeInNewImage:overlayImage]; // 3
});
});
使用 dispatch_after 延后工作
一旦 dispatch_after 返回也就不能取消它,最好主线程操作
double delayInSeconds = 1.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)); // 1
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){ // 2
if (!count) {
[self.navigationItem setPrompt:@"Add photos with faces to Googlyify them!"];
} else {
[self.navigationItem setPrompt:nil];
}
});
使用group处理依赖
- (void)group {
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();
dispatch_group_enter(group);
dispatch_group_async(group, queue, ^{
[NSThread sleepForTimeInterval:1];
NSLog(@"group1");
dispatch_group_leave(group);
});
dispatch_group_enter(group);
dispatch_group_async(group, queue, ^{
[NSThread sleepForTimeInterval:2];
NSLog(@"group2");
dispatch_group_leave(group);
});
dispatch_group_enter(group);
dispatch_group_async(group, queue, ^{
[NSThread sleepForTimeInterval:3];
NSLog(@"group3");
dispatch_group_leave(group);
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"updateUi");
});
}
使用barrier
queue必须是DISPATCH_QUEUE_CONCURRENT类型的.queue如果是global或者不是DISPATCH_QUEUE_CONCURRENT类型无效
- (void)barrier {
dispatch_queue_t queue = dispatch_queue_create("test.barrier.com", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
[NSThread sleepForTimeInterval:2];
NSLog(@"dispatch_async1");
});
dispatch_async(queue, ^{
[NSThread sleepForTimeInterval:4];
NSLog(@"dispatch_async2");
});
dispatch_barrier_async(queue, ^{
NSLog(@"dispatch_barrier_async");
[NSThread sleepForTimeInterval:4];
});
dispatch_async(queue, ^{
[NSThread sleepForTimeInterval:1];
NSLog(@"dispatch_async3");
});
}
使用apply
- (void)dispatchApplyTest {
//生成全局队列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
/*! dispatch_apply函数说明
*
* @brief dispatch_apply函数是dispatch_sync函数和Dispatch Group的关联API
* 该函数按指定的次数将指定的Block追加到指定的Dispatch Queue中,并等到全部的处理执行结束
*
* @param 10 指定重复次数 指定10次
* @param queue 追加对象的Dispatch Queue
* @param index 带有参数的Block, index的作用是为了按执行的顺序区分各个Block
*
*/
dispatch_apply(10, queue, ^(size_t index) {
NSLog(@"%zu", index);
});
NSLog(@"done");
/*!
* @brief 输出结果
*
2016-02-25 19:24:39.102 dispatch_apply测试[2985:165004] 0
2016-02-25 19:24:39.102 dispatch_apply测试[2985:165086] 1
2016-02-25 19:24:39.104 dispatch_apply测试[2985:165004] 4
2016-02-25 19:24:39.104 dispatch_apply测试[2985:165004] 5
2016-02-25 19:24:39.104 dispatch_apply测试[2985:165004] 6
2016-02-25 19:24:39.103 dispatch_apply测试[2985:165088] 3
2016-02-25 19:24:39.104 dispatch_apply测试[2985:165004] 7
2016-02-25 19:24:39.105 dispatch_apply测试[2985:165004] 8
2016-02-25 19:24:39.105 dispatch_apply测试[2985:165004] 9
2016-02-25 19:24:39.102 dispatch_apply测试[2985:165087] 2
2016-02-25 19:24:39.105 dispatch_apply测试[2985:165004] done
* !!!因为在Global Dispatch Queue中执行,所以各个处理的执行时间不定
但done一定会输出在最后的位置,因为dispatch_apply函数会等待所以的处理结束
*/
}
常见锁的使用:
为什么需要锁
锁的种类
OSSpinLock
- OSSpinLock叫做”自旋锁”,等待锁的线程会处于忙等(busy-wait)状态,一直占用着CPU资源
- 目前已经不再安全,可能会出现优先级反转问题
- 如果等待锁的线程优先级较高,它会一直占用着CPU资源,优先级低的线程就无法释放锁
- 需要导入头文件#import
具体来说,如果一个低优先级的线程获得锁并访问共享资源,这时一个高优先级的线程也尝试获得这个锁,它会处于 spin lock 的忙等状态从而占用大量 CPU。此时低优先级线程无法与高优先级线程争夺 CPU 时间,从而导致任务迟迟完不成、无法释放 lock。
- (void)用法介绍 {
//init
OSSpinLock lock = OS_SPINLOCK_INIT;
// 尝试加锁,无需等待,直接加锁,返回YES.如果需要等待就不加锁,返回NO,
BOOL result = OSSpinLockTry(&lock);
// 加锁
OSSpinLockLock(&lock);
// 解锁
OSSpinLockUnlock(&_moneyLock);
}
os_unfair_lock
- os_unfair_lock用于取代不安全的OSSpinLock ,从iOS10开始才支持
- 从底层调用看,等待os_unfair_lock锁的线程会处于休眠状态,并非忙等
- 需要导入头文件#import
- (void)用法介绍 {
//init
os_unfair_lock lock = OS_UNFAIR_LOCK_INIT;
// 尝试加锁,无需等待,直接加锁,返回YES.如果需要等待就不加锁,返回NO,
BOOL result = os_unfair_lock_trylock(&lock);
// 加锁
os_unfair_lock_lock(&lock);
// 解锁
os_unfair_lock_unlock(&_moneyLock);
}
pthread_mutex
- mutex叫做”互斥锁”,等待锁的线程会处于休眠状态
- 需要导入头文件#import
- (void)用法介绍 {
//初始化属性
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
/*
* Mutex type attributes
#define PTHREAD_MUTEX_NORMAL 0
#define PTHREAD_MUTEX_ERRORCHECK 1
#define PTHREAD_MUTEX_RECURSIVE 2
#define PTHREAD_MUTEX_DEFAULT PTHREAD_MUTEX_NORMAL
*/
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_DEFAULT);
//初始化锁
pthread_mutex_t lock;
pthread_mutex_init(&lock, &attr);
// 尝试加锁
pthread_mutex_trylock(&lock);
// 加锁
pthread_mutex_lock(&lock);
// 解锁
pthread_mutex_unlock(&lock);
// 销毁资源
pthread_mutexattr_destroy(&attr);
pthread_mutex_destroy(&lock);
}
pthread_mutex递归锁
#pragma mark - 递归锁
- (void)__initRecursiveMutex:(pthread_mutex_t *)mutex
{
// 递归锁:允许同一个线程对一把锁进行重复加锁
// 初始化属性
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
// 初始化锁
pthread_mutex_init(mutex, &attr);
// 销毁属性
pthread_mutexattr_destroy(&attr);
}
// 同一个线程可以重复加锁
- (void)recursiveTest {
pthread_mutex_lock(&_recursive_mutex);
NSLog(@"%s", __func__);
static int count = 0;
if (count < 10) {
count++;
[self recursiveTest];
}
pthread_mutex_unlock(&_recursive_mutex);
}
pthread_mutex条件锁
- (void)用法介绍 {
// 初始化锁
pthread_mutex_t mutex;
pthread_mutex_init(&mutex, NULL);
// 初始化条件
pthread_cond_t conditon;
pthread_cond_init(&conditon, NULL);
// 等待条件,进入休眠,放开锁。唤醒后再次对mutex加锁
pthread_cond_wait(&conditon, &mutex);
// 激活一个等待条件对的线程
pthread_cond_signal(&conditon);
// 激活所有等待条件得到线程
pthread_cond_broadcast(&conditon);
//销毁资源
pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&conditon);
}
- (void)coditionTest {
[[[NSThread alloc] initWithTarget:self selector:@selector(__remove) object:nil] start];
[[[NSThread alloc] initWithTarget:self selector:@selector(__add) object:nil] start];
}
// 删除数组中的元素
- (void)__remove {
pthread_mutex_lock(&_mutex);
NSLog(@"__remove - begin");
if (self.data.count == 0) {
// 等待
pthread_cond_wait(&_conditon, &_mutex);
}
[self.data removeLastObject];
NSLog(@"删除了元素");
pthread_mutex_unlock(&_mutex);
}
// 往数组中添加元素
- (void)__add {
pthread_mutex_lock(&_mutex);
sleep(1);
[self.data addObject:@"Test"];
NSLog(@"添加了元素");
pthread_cond_signal(&_conditon);
pthread_mutex_unlock(&_mutex);
}
NSLock & NSRecursiveLock
NSLock
是对mutex的普通锁封装
- (void)用法介绍 {
NSLock *lock = [[NSLock alloc] init];
[lock tryLock];
[lock lockBeforeDate:[NSDate distantFuture]];
[lock lock];
[lock unlock];
NSRecursiveLock *recursiveLock = [[NSRecursiveLock alloc] init];
[recursiveLock tryLock];
[recursiveLock lockBeforeDate:[NSDate distantFuture]];
[recursiveLock lock];
[recursiveLock unlock];
}
NSCondition
NSCondition是对mutex和cond的封装
- (void)用法介绍 {
NSCondition *condition = [[NSCondition alloc] init];
[condition wait];
[condition waitUntilDate:[NSDate distantFuture]];
[condition signal];
[condition broadcast];
[condition lock];
[condition unlock];
}
- (void)coditionTest {
[[[NSThread alloc] initWithTarget:self selector:@selector(__remove) object:nil] start];
[[[NSThread alloc] initWithTarget:self selector:@selector(__add) object:nil] start];
}
- (void)__remove {
[self.condition lock];
NSLog(@"__remove - begin");
if (self.data.count == 0) {
// 等待
[self.condition wait];
}
[self.data removeLastObject];
NSLog(@"删除了元素");
[self.condition unlock];
}
- (void)__add {
[self.condition lock];
sleep(1);
[self.data addObject:@"Test"];
NSLog(@"添加了元素");
// 信号
[self.condition signal];
sleep(2);
[self.condition unlock];
}
NSConditionLock
NSConditionLock是对NSCondition的进一步封装,可以设置具体的条件值condition
- (void)用法介绍 {
NSConditionLock *lock = [[NSConditionLock alloc] initWithCondition:1];
NSInteger cod = [lock condition];//readOnly
[lock lock];//不管cod,直接加锁
[lock lockWhenCondition:1];
[lock tryLock];
[lock tryLockWhenCondition:1];
[lock lockBeforeDate:[NSDate distantFuture]];
[lock lockWhenCondition:1 beforeDate:[NSDate distantFuture]];
[lock unlock];//不管cod,直接解锁
[lock unlockWithCondition:1];
}
- (void)coditionLockTest {
[[[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 lock];
NSLog(@"__one");
sleep(1);
[self.conditionLock unlockWithCondition:2];
}
- (void)__two {
[self.conditionLock lockWhenCondition:2];
NSLog(@"__two");
sleep(1);
[self.conditionLock unlockWithCondition:3];
}
- (void)__three {
[self.conditionLock lockWhenCondition:3];
NSLog(@"__three");
[self.conditionLock unlock];
}
dispatch_semaphore
- semaphore叫做”信号量”
- 信号量的初始值,可以用来控制线程并发访问的最大数量
- 信号量的初始值设为1,代表同时只允许1条线程访问资源,保证线程同步
- 相关代码在libdispatch
- (void)用法介绍 {
// 初始化 信号量=1,可以控制并发数量
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
// 信号量-1
// 如果信号量>0,继续执行。如果信号量<=0,当前线程休眠
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);//time=等待时间
// 信号量+1
dispatch_semaphore_signal(self.moneySemaphore);
}
- (void)__发红包 {
dispatch_semaphore_wait(self.moneySemaphore, DISPATCH_TIME_FOREVER);
[super __发红包];
dispatch_semaphore_signal(self.moneySemaphore);
}
@synchronized
- @synchronized是对mutex递归锁的封装
- 源码查看:objc4中的objc-sync.mm文件
- @synchronized(obj)内部会生成obj对应的递归锁,然后进行加锁、解锁操作
@synchronized(self) {
}
dispatch_queue(DISPATCH_QUEUE_SERIAL)
直接使用GCD的串行队列,也是可以实现线程同步的
- (void)__卖票 {
dispatch_sync(self.moneyQueue, ^{
[super __卖票];
});
}
汇编指令暂存
c 下个断点
s step 一行OC代码
si stepinstruction 一行指令 汇编
nexti 函数
j 跳转 ne 条件
回车为上一个指令
性能对比
性能从高到低排序
os_unfair_lock(iOS10开始支持)
OSSpinLock(优先级反转)
dispatch_semaphore
pthread_mutex
dispatch_queue(DISPATCH_QUEUE_SERIAL)
NSLock
NSCondition
pthread_mutex(recursive)
NSRecursiveLock
NSConditionLock
@synchronized
互斥和自旋
-
什么情况使用自旋锁比较划算?
- 预计线程等待锁的时间很短
- 加锁的代码(临界区)经常被调用,但竞争情况很少发生
- CPU资源不紧张
- 多核处理器
-
什么情况使用互斥锁比较划算?
- 预计线程等待锁的时间较长
- 单核处理器
- 临界区有IO操作
- 临界区代码复杂或者循环量大
- 临界区竞争非常激烈
读写锁(I/O)
多读单写:
- 同一时间,只能有1个线程进行写的操作
- 同一时间,允许有多个线程进行读的操作
- 同一时间,不允许既有写的操作,又有读的操作
pthread_rwlock_t
等待锁的线程会进入休眠
- (void)用法介绍 {
pthread_rwlock_t lock;
pthread_rwlock_init(&lock,NULL);
pthread_rwlock_rdlock(&lock);
pthread_rwlock_tryrdlock(&lock);
pthread_rwlock_wrlock(&lock);
pthread_rwlock_trywrlock(&lock);
pthread_rwlock_unlock(&lock);
pthread_rwlock_destroy(&lock);
}
- (void)read {
pthread_rwlock_rdlock(&_lock);
sleep(1);
NSLog(@"--%s-- IN thread:%@",__func__,[NSThread currentThread]);
pthread_rwlock_unlock(&_lock);
}
- (void)write {
pthread_rwlock_wrlock(&_lock);
sleep(1);
NSLog(@"--%s-- IN thread:%@",__func__,[NSThread currentThread]);
pthread_rwlock_unlock(&_lock);
}
dispatch_barrier_async
这个函数传入的并发队列必须是自己通过dispatch_queue_cretate创建的
如果传入的是一个串行或是一个全局的并发队列,那这个函数便等同于dispatch_async函数的效果
- (void)read {
dispatch_async(self.queue, ^{
sleep(1);
NSLog(@"--%s-- IN thread:%@",__func__,[NSThread currentThread]);
});
}
- (void)write
dispatch_barrier_async(self.queue, ^{
sleep(1);
NSLog(@"--%s-- IN thread:%@",__func__,[NSThread currentThread]);
});
}
GCD原理:
其他:
参考
iOS多线程编程的几种方式
iOS进阶之多线程--NSThread详解
自定义NSOperation
彻底了解NSOperation的自定义
深入理解GCD之dispatch_group
GCD 深入理解
GNUstep
libdispatch
Jason's Blog,@synchronized 是递归锁
objc4
不再安全的 OSSpinLock