美文网首页
NSThread/NSOperation/GCD的优缺点总结

NSThread/NSOperation/GCD的优缺点总结

作者: 私人云笔记_骁勇波波 | 来源:发表于2018-03-12 13:08 被阅读0次

    转自: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则更有优势

    相关文章

      网友评论

          本文标题:NSThread/NSOperation/GCD的优缺点总结

          本文链接:https://www.haomeiwen.com/subject/dvsnfftx.html