NSThread/NSOperation/GCD的优缺点总结
转自:http://blog.csdn.net/nathan1987_/article/details/50436132
•NSThread:
–优点:NSThread 比其他两个轻量级,使用简单
–缺点:需要自己管理线程的生命周期、线程同步、加锁、睡眠以及唤醒等。线程同步对数据的加锁会有一定的系统开销
[objc] view plain copy
//创建线程方法1
NSThread *thread = [[NSThread alloc]initWithTarget:self selector:@selector(threadAction:) object:nil];
[thread start];//开启子线程
[thread cancel];//取消子线程
//创建线程方法2-立即在线程中执行任务
[NSThread detachNewThreadSelector:@selector(threadAction:) toTarget:self withObject:nil];
//创建线程方法3-在后台子线程中执行任务
[self performSelectorInBackground:@selector(threadAction:) withObject:nil];
-(void)threadAction:(id*)sender{
@autoreleasepool {
//子线程中通知主线程通常使用以下两种办法
// [self.imageview performSelectorOnMainThread:@selector(updateView:) withObject:nil waitUntilDone:YES];
// [self.imageview performSelector:@selector(updateView:) onThread:[NSThread mainThread] withObject:nil waitUntilDone:YES];
}
}
线程间通讯
线程下载完图片后怎么通知主线程更新界面呢?
performSelectorOnMainThread是NSObject的方法,除了可以更新主线程的数据外,还可以更新其他线程的
两种锁,一种NSCondition ,一种是:NSLock
// 锁对象
theLock = [[NSLock alloc] init];
ticketsCondition = [[NSCondition alloc] init];
// 上锁
// [ticketsCondition lock];
[theLock lock];
//中间写代码
//开锁
// [ticketsCondition unlock];
[theLock unlock];
可以通过[ticketsCondition signal]; 发送信号的方式,在一个线程唤醒另外一个线程的等待。
NSCondition的wait其实就是在线程内等待一个信号量, 信号量出现时就继续, 否则一直等下去
也可以用- (BOOL)waitUntilDate:(NSDate *)limit;
这个在给定的时间到达时仍未有信号量出现, 就自动继续了.
如果用户给出信号量来触发继续的话, 会返回1
如果超时触发继续, 返回0
theLock = [[NSLock alloc] init];
// 锁对象
ticketsCondition = [[NSCondition alloc] init];
[ticketsCondition lock];
[NSThread sleepForTimeInterval:3];
[ticketsCondition signal]; //唤醒另一个线程
[ticketsCondition unlock];
// 上锁
[ticketsCondition lock];
[ticketsCondition wait];
[theLock lock];
//要做的事情
[theLock unlock];
[ticketsCondition unlock];
其他同步
我们可以使用指令 @synchronized 来简化 NSLock的使用,这样我们就不必显示编写创建NSLock,加锁并解锁相关代码。
@synchronized(anObj)
{
// Everything between the braces is protected by the @synchronized directive.
}
还有其他的一些锁对象,
比如:循环锁NSRecursiveLock,条件锁NSConditionLock,分布式锁NSDistributedLock等等,可以自己看官方文档学习
•NSOperation:
–不需要关心线程管理,数据同步的事情,可以把精力放在自己需要执行的操作上
–NSOperation是面向对象的
//NSOperationQueue
//两种操作-(操作本身跟多线程关系不大)
//NSInvocationOperation
//NSBlockOperation
NSInvocationOperation *inop = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(inopAction:) object:nil];
//[inop start];
NSBlockOperation *blop = [NSBlockOperation blockOperationWithBlock:^{
@autoreleasepool { NSLog(@"blop"); } }];
//队列
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
//1的话顺序执行
//同时执行最大操作数
queue.maxConcurrentOperationCount = 3;
//依赖关系
[inop addDependency:blop];//blop执行完,才能执行inop
//向队列添加操作
[queue addOperation:inop];
[queue addOperation:blop];
-(void)inopAction:(id)sender{
@autoreleasepool { NSLog(@"inop"); }
}
关于并发数
(1)并发数:同时执⾏行的任务数.比如,同时开3个线程执行3个任务,并发数就是3
(2)最大并发数:同一时间最多只能执行的任务的个数。
(3)最⼤大并发数的相关⽅方法
- (NSInteger)maxConcurrentOperationCount;
- (void)setMaxConcurrentOperationCount:(NSInteger)cnt;
说明:如果没有设置最大并发数,那么并发的个数是由系统内存和CPU决定的,可能内存多久开多一点,内存少就开少一点。
注意:num的值并不代表线程的个数,仅仅代表线程的ID。
提示:最大并发数不要乱写(5以内),不要开太多,一般以2~3为宜,因为虽然任务是在子线程进行处理的,但是cpu处理这些过多的子线程可能会影响UI,让UI变卡。
•GCD:
–Grand Central Dispatch是由苹果开发的一个多核编程的解决方案。iOS4.0+才能使用,是替代NSThread, NSOperation的高效和强大的技术
–GCD是基于C语言的
GCD的工作原理是:让程序平行排队的特定任务,根据可用的处理资源,安排他们在任何可用的处理器核心上执行任务。
一个任务可以是一个函数(function)或者是一个block。 GCD的底层依然是用线程实现,不过这样可以让程序员不用关注实现的细节。
GCD中的FIFO队列称为dispatch queue,它可以保证先进来的任务先得到执行
dispatch queue分为下面三种:
Serial
又称为private dispatch queues,同时只执行一个任务。Serial queue通常用于同步访问特定的资源或数据。当你创建多个Serial queue时,虽然它们各自是同步执行的,但Serial queue与Serial queue之间是并发执行的。
Concurrent
又称为global dispatch queue,可以并发地执行多个任务,但是执行完成的顺序是随机的。
Main dispatch queue
它是全局可用的serial queue,它是在应用程序主线程上执行任务的。
The main queue: 与主线程功能相同。实际上,提交⾄至main queue的任务会在主线程中执⾏行。main queue可以调⽤用dispatch_get_main_queue()来获得。因为mainqueue是与主线程相关的,所以这是⼀一个串⾏行队列。
Global queues: 全局队列是并发队列,并由整个进程共享。进程中存在三个全局队列:⾼高、中(默认)、低、后台四个优先级队列。可以调⽤用dispatch_get_global_queue函数传⼊入优先级来访问队列。优先级:
#define DISPATCH_QUEUE_PRIORITY_HIGH 2
#define DISPATCH_QUEUE_PRIORITY_DEFAULT 0
#define DISPATCH_QUEUE_PRIORITY_LOW (-2)
#define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN
dispatch_sync(),同步添加操作。等待添加进队列里面的操作完成之后再继续执行。调用以后等到block执行完以后才返回 ,dispatch_sync()会阻塞当前线程。
dispatch_async ,异步添加进任务队列,调用以后立即返回,它不会做任何等待
在多线程开发当中,程序员只要将想做的事情定义好,并追加到DispatchQueue(派发队列)当中就好了。
派发队列分为两种,一种是串行队列(SerialDispatchQueue),一种是并行队列(ConcurrentDispatchQueue)。
一个任务就是一个block,比如,将任务添加到队列中的代码是:
1 dispatch_async(queue, block);
当给queue添加多个任务时,如果queue是串行队列,则它们按顺序一个个执行,同时处理的任务只有一个。
当queue是并行队列时,不论第一个任务是否结束,都会立刻开始执行后面的任务,也就是可以同时执行多个任务。
但是并行执行的任务数量取决于XNU内核,是不可控的。比如,如果同时执行10个任务,那么10个任务并不是开启10个线程,线程会根据任务执行情况复用,由系统控制。
延时的实现
[objc] view plain copy
//第一种NSThread延时
[NSThread sleepForTimeInterval:3];//延时3秒-阻塞主线程
//第二种
[self performSelector:@selector(dosth) withObject:nil afterDelay:2];//延时3秒执行,不会阻塞主线程
//第三种GCD 3秒回到主线程执行 不会阻塞主线程
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(33 * NSEC_PER_SEC)),
dispatch_get_main_queue(), ^{NSLog(@"第三种");} );
//第四种 GCD
dispatch_queue_t qq = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)4*NSEC_PER_SEC), qq, ^{
NSLog(@"第四种");
});
-(void)dosth{
NSLog(@"第二种");
}
dispatch_barrier_async的使用
dispatch_barrier_async是在前面的任务执行结束后它才执行,而且它后面的任务等它执行完成之后才会执行
dispatch_group_async的使用
dispatch_group_async可以实现监听一组任务是否完成,完成后得到通知执行其他的操作。这个方法很有用,比如你执行三个下载任务,当三个任务都下载完成后你才通知界面说完成的了。
[objc] view plain copy
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, queue, ^{
[NSThread sleepForTimeInterval:1];
NSLog(@"group1");
});
dispatch_group_async(group, queue, ^{
[NSThread sleepForTimeInterval:2];
NSLog(@"group2");
});
dispatch_group_async(group, queue, ^{
[NSThread sleepForTimeInterval:3];
NSLog(@"group3");
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"updateUi");
});
dispatch_release(group);
[objc] view plain copy
//重复执行
//放到全局队列才执行
dispatch_apply(10, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(size_t t) {
NSLog(@"重复执行,%ld",t);
});
Operation、GCD对比:
优点:不需要关心线程管理,数据同步的事情。
两者区别:
NSOperationQueue可以方便的管理并发、NSOperation之间的优先级。
GCD主要与block结合使用。代码简洁高效
1. 性能:GCD更接近底层,而NSOperationQueue则更高级抽象,所以GCD在追求性能的底层操作来说,是速度最快的。这取决于使用Instruments进行代码性能分析,如有必要的话
2. 从异步操作之间的事务性,顺序行,依赖关系。GCD需要自己写更多的代码来实现,而NSOperationQueue已经内建了这些支持
3. 如果异步操作的过程需要更多的被交互和UI呈现出来,NSOperationQueue会是一个更好的选择。底层代码中,任务之间不太互相依赖,而需要更高的并发能力,GCD则更有优势