iOS面试题11- NSThread/NSOperation/GCD的优缺点总结

iOS面试题11- NSThread/NSOperation/GCD的优缺点总结




•NSThread:
–优点:NSThread 比其他两个轻量级,使用简单
–缺点:需要自己管理线程的生命周期、线程同步、加锁、睡眠以及唤醒等。线程同步对数据的加锁会有一定的系统开销

//创建线程方法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个线程,线程会根据任务执行情况复用,由系统控制。



延时的实现

 //第一种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)(3 * 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可以实现监听一组任务是否完成,完成后得到通知执行其他的操作。这个方法很有用,比如你执行三个下载任务,当三个任务都下载完成后你才通知界面说完成的了。


    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);


  //重复执行
    //放到全局队列才执行
    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则更有优势


著作权声明:本文由http://www.bestnathan.com/原创,欢迎转载分享。请尊重作者劳动,转载时保留该声明和作者博客链接,谢谢

你可能感兴趣的:(面试)