iOS线程浅析
一、线程概述
1、 iOS里面的线程按种类可分为同步线程和异步线程。同步线程指调用同步线程的地方必须等到同步线程执行完毕才可以继续向下执行。而调用异步线程的地方则在执行完调用异步线程的语句后就可以继续向下执行。
2、线程按调用方式又可以大致分为以下几种类型:NSObject、NSThread、NSOperation和GCD。NSObject和NSThread只能管理单个的线程,功能较简单,GCD和NSOperation则可以进行队列等复杂操作,且效率较高。其中GCD方式最为有效,NSOperation是基于GCD封装的,功能相对来说更丰富。
3、异步线程中往往要创建自己的自动释放池来释放异步线程中创建的自动释放对象。
4、异步线程中有需要的话,要做自己的异常捕捉处理,因为当异步线程中产生异常时,主线程很有可能会捕捉不到异步线程抛出的异常。
二、线程调用
1、NSObject
* 线程创建
NSObjcet对象自身实现了performSelectorInBackground、performSelectorOnMainThread和performSelector:onThread:withObject:等线程相关方法。
1.1 performSelectorInBackground是创建一个异步线程
-(void)performSelectorInBackground:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait :
aSelector是在主线程中执行的方法,arg是传递的参数,wait指是否要等待aSelector执行完毕。
1.2 performSelectorOnMainThread是返回主线程执行相关方法
-(void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait:
aSelector是在主线程中执行的方法,arg是传递的参数,wait指是否要等待aSelector执行完毕。
1.3 performSelector:onThread:withObject:是执行某个特定线程中的方法
-(void)performSelector:(SEL)aSelector onThread:(NSThread *) withObject:(id)arg waitUntilDone:(BOOL)wait:
该方法一般用于线程间相互通信,即在一个线程中发送消息给另一个线程
注:每一个线程都有自己的RunLoop,但非主线程的RunLoop默认是关闭的,当需要进行非主线程的通信时,需要确保通信线程的RunLoop是开启的,否则发送给通信线程的消息不会被执行。
1.4 -(void)performSelector:withObject:afterDelay:是指在当前线程延迟执行某个方法
* 线程的暂停、继续和取消
NSObject是无法暂停和取消线程的。
2、NSThread
* 线程创建
NSThread有两种创建线程的方式: detachNewThreadSelector和initWithTarget.
2.1 detachNewThreadSelector:toTarget: withObject:
调用这种方法创建的线程会主动运行,不需要手动启动。
2.2 initWithTarget: selector:objcet:
调用这种方式新建一个线程,新建完成后需要调用NSThread的start方法来启动线程。
* 线程的取消
调用NSThrea的cancel方法即可取消线程,但正在执行的线程不会马上结束。所以NSThread的cancel方法的实质意义是把线程的状态设置为已取消,然后编代码时就可以依赖线程的状态去结束掉线程。
* 线程的暂停和继续
NSThread没有提供暂停的方法
3、NSOperation
* 线程创建
NSOperation通常和NSOperationQueue配对使用,创建好NSOperation对象后添加到NSOperationQueue中,NSOperationQueue即可为我们管理NSOperation,包括为NSOperation配置异步线程和启动等。
NSOperationQueue按类型分为:主队列和自定义队列。主队列运行在主线程上,而自定义队列在后台执行。主队列获取方式:[NSOperationQueue mainQueue];自定义队列获取方式:[[NSOperationQueue alloc] init]。
NSOperation按类型分为:NSOperation、NSBlockOperation和NSInvocationOperation。
3.1 NSOperation:
NSOperation是一个虚类,我们需要继续NSOperation来创建子类,并重写main方法或start方法。重写main方法很简单,只需要重写好main方法,不需要管理一些状态属性(如isExecuted和isFinished),当main方法返回就假定操作结束了;而重写start方法则更灵活,拥有更多的控制权,但是状态属性信息则需要自己控制。
3.2 NSBlockOperation:
NSBlockOperation是调用blockOperationWithBlock方法以一个block作为参数创建的一个operation对象,并且在对象实例化后还可以调用addExecutionBlock方法动态添加block,但添加动作需要在operation执行前发生,通常也就是在operation添加到NSOperationQueue前,否则很可能产生异常和莫名错误。NSBlockOperation的业务逻辑操作主要写在block中。
3.3 NSInvocationOperation:
NSInvocationOperation是以一个SEL方法或NSInvocation为参数初始化的operation对象。NSInvocationOperation的业务逻辑操作主要放在对应的SEL方法或NSInvocation对象中。
* 线程的暂定和继续
调用NSOperationQueue对象的setSuspended:YES即可暂停NSOperationQueue中待执行线程的执行,然后调用setSuspended:NO即可继续执行。
* 线程的取消
调用NSOperation对象的cancel方法会取消单个NSOperation对象的执行,而调用NSOperationQueue对象的cancelAllOperation方法会取消NSOperation队列中所有对象的执行。
* 线程依赖
如果NSOperation对象有执行顺利要求的话,比如operationB需要在operationA执行完毕后才可以执行,那就可以通过设置NSOperation之间的依赖关系来实现:[operationB addDependency:operationA]。
* 手动执行
如果NSOperation不和NSOperationQueue配对使用,这时就需要调用NSOperation的start方法来执行NSOperation,通常要重写isExecuting,isFinished,start和main等方法,如果要实现并发操作,则还要重写isConcurrent方法,如果NSOperation间有依赖关系,则还要重写isReady方法。示例如下:
@interface HandOperation(){
BOOL executing; // 是否正在执行
BOOL finished;// 是否执行结束
}
-(void)completeOperation;// 结束后的状态处理方法
@end
@implementation HandOperation
#pragma mark - Lifecycle Methods
-(id)init{
self = [super init];
if(self){
executing = NO;
finished = NO;
}
return self;
}
-(BOOL)isConcurrent{
return YES;
}
-(BOOL)isExecuting{
return executing;
}
-(BOOL)isFinished{
return finished;
}
-(BOOL)isReady{
return [super isReady];
}
-(void)start{
if([self isCancelled]){
[self willChangeValueForKey:@“isFinished”];
finished = YES;
[self didChangeValueForKey:@“isFinished”];
return;
}
[self willChangeValueForKey:@“isExecuting”];
executing = YES;
[NSThread detachNewThreadSelector:@selector(main) toTarget:self withObject:nil];
[self didChangeValueForKey:@“isExecuting”];
}
-(void)main{
@try{
@autoraleasepool{
[self handleForTask];
}
[self completeOperation];
}
@catch(NSException *exception){
}
@finally{
}
}
#pragma mark - Custom Methods
-(void)completeOperation{
[self willChangeValueForKey:@“isFinished”];
[self willChangeValueForKey:@“isExecuting”];
executing = NO;
finished = YES;
[self didChangeValueForKey:@“isExecuting”];
[self didChangeValueForKey:@“isFinished”];
}
//业务处理方法
-(void)handleForTask{
for(int i=0;i<10;i++){
if(![self isCancelled]){
NSLog(@“%s %i”,__func__,i);
sleep(1);
}
}
}
4、GCD
* 线程创建
GCD是Grand Central Dispatch的缩写,是Apple开发的一个多核编程的解决方案。GCD通常是把block以dispatch_async或dispatch_sync的方式添加到dispatch_queue_t队列中来创建线程: dispatch_async(dispatch_queue_t queue ,dispatch_block block)。
dispatch_async为异步添加,等dispatch_async语句执行完毕后即可继续执行下去;而dispatch_sync为同步添加,需要等dispatch_sync添加的block内容执行完毕后才可以继续向下执行。
dispatch_queue_t分为三种类型: Serial Queues、Main Queues和Concurrent Queues。
4.1 Serial Queues
Serial Queues称为串行队列,Serial Queues队列中的任务只能顺序执行,一次执行一个。我们可以在Serial Queues中执行有依赖关系的任务或同步任务。
Serial Queues队列之间是可以并发执行的。
Serial Queues是通过dispatch_queue_create(“testQueue”,NULL)语句创建的,“testQueue”是队列标识符,NULL对应一个表示队列属性的参数,只要传入NULL或DISPATCH_QUEUE_SERIAL即可获取一个串行队列。
4.2 Main Queues
Main Queues 称为主线程队列,所有提交至Main Queues中的任务都会在主线程中执行,所以Main Queues也是一个串行队列。
Main Queues可以通过dispatch_get_main_queue()语句获取到,Main Queues是全局性的队列。
4.3 Concurrent Queues
Concurrent Queues称为并行队列,所有提交至Concurrent Queues中的任务都是并行执行的。Concurrent Queues又分为Global Queues和User Concurrent Queues。Global Queues由系统根据优先级提供四个不同的dispatch queue。
Global Queues可以通过dispatch_get_global_queue(dispatch_queue_priority_t priority,unsigned long flags)语句获取到,priority是优先级,优先级有DISPATCH_QUEUE_PRIORITY_HIGH、DISPATCH_QUEUE_PRIORITY_DEFAULT、DISPATCH_QUEUE_PRIORITY_LOW、DISPATCH_QUEUE_PRIORITY_BACKGROUND四种:flags是保留字段,现阶段传入0即可。
User Concurrent Queues是通过dispatch_queue_create(“testQueue”,DISPATCH_QUEUE_CONCURRENT)语句创建的,“testQueue”是队列标识符,DISPATCH_QUEUE_CONCURRENT表示队列为并行队列。
* 线程的暂停和继续
调用dispatch_suspend(dispatch_queue_t)即可挂起队列,使队列中待执行的任务暂停执行,但正在执行的任务还是会继续执行。
调用dispatch_resume(dispatch_queue_t)即可以使挂起的队列继续执行。
dispatch_suspend 和 dispatch_resume只对Serial Queues 和 User Concurrent Queues有效,因为Main Queues 和Global Queues都是全局队列。
* 线程的取消
GCD中没有显式的线程取消方法调用,只能在代码中根据预先设置的标志位去实现取消操作。
* dispatch_group_async的使用
dispatch_group_async可以实现监听一组任务是否完成,完成后得到通知执行其他操作。
使用dispatch_group_async添加一组任务后,可以使用dispatch_group_wait方法同步等待全部任务执行完毕,然后才执行之后的其他任务;也可以使用dispatch_group_notify异步监听前面的任务,等前面任务执行完毕后触发dispatch_group_notify中的处理事件。如下:
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group,globalQueue,^{
@autoreleasepool{
for(int i=0;i<5;i++){
NSLog(@“001 %i”,i);
sleep(1);
}
}
});
dispatch_group_async(group,globalQueue,^{
@autoreleasepool{
for(int i=0;i<5;i++){
NSLog(@“002 %i”,i);
sleep(1);
}
}
});
dispatch_group_wait(group,DISPATCH_TIME_FOREVER);
/*
dispatch_group_notify(group,globalQueue,^{
@qutoreleasepool{
NSLog(@“notify End”);
}
});
*/
dispatch_release(group);
* dispatch_barrier_async的使用
dispatch_barrier_async是在前面的任务执行结束后它才执行,而且它后面的任务等它执行完成后才执行。
dispatch_barrier_async(dispatch_queue_t queue,dispatch_block_t block),queue必须是自定义的并行队列,如果是其他队列,那么dispatch_barrier_async的效果和dispatch_sync是相同的。
* dispatch_apply 的使用
dispatch_apply可以执行某个代码片段N次,可以用在相互间没依赖关系的并行计算等。代码如下:
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_PRIORITY_DEFAULT,0);
__block int sum = 0;
dispatch_apply(10,globalQueue,^(size_t i){
sum += i;
});