zoukankan      html  css  js  c++  java
  • iOS多线程NSThread/GCD/NSOperation区别和使用

    概述:

    1.NSThread 一般用做调试用,需要程序员管理生命周期,开发中较少使用。

    2.GCD(iOS 4.0) 由系统管理,开发中使用的很多。

    3.NSOperation(iOS 2.0) 基于GCD的OC封装,开发中使用的较多。

    • GCD(grand central dispatch)

    核心概念:同步/异步,全局队列/主队列

     全局队列:
     {
        同步:不开
        异步:开N条
     }
     
     主队列(奇葩):
     {
            同步:卡死,不要用
            异步:不开,因为他有主线程
     }
     
     /**
        开不开线程线程由任务是同步还是异步
            同步:打死都不开
            异步:除了主队列,都开,开多少条,由我们的队列的类型来决定
     */

    使用方式1:异步下载图片,回到主线程更新UI

        //一般下载,可能会下载多个
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            NSLog(@"downLoad----%@",[NSThread currentThread]);
            //去做耗时间的操作
            
            //下载图像...最后得到一个UIImage
            
            //去主线程更新UI
            dispatch_async(dispatch_get_main_queue(), ^{
                NSLog(@"updateUI----%@",[NSThread currentThread]);
            });
            
        });

    使用方式2:线程安全-设置依赖关系(例如你异步下载20张图片写入一个数组中,为了保证效率所以数组为非线程安全的,如果解决多线程同时访问的问题)

    解决办法是间接通过GCD的阻塞(dispatch_barrier_async)和同步

    - (void)viewDidLoad {
        [super viewDidLoad];
        // Do any additional setup after loading the view, typically from a nib.
        
        //创建一个并发的队列
        /**
            注意:如果你用了dispatch_barrier_async必须要用我们自己创建的并发队列,而不能用全局队列
         */
        _concurrentQueue = dispatch_queue_create("com.yun", DISPATCH_QUEUE_CONCURRENT);
        
        //循环去请求20个图片资源
        for (int i=0; i<20; ++i) {
            [self loadPic:i];
        }
    }
    
    - (void)loadPic:(int)i{
        //模拟网络,通过URL去访问Bundle里面的图片
        dispatch_async(_concurrentQueue, ^{
            [NSThread sleepForTimeInterval:1.0];
            
            NSString *fileName = [NSString stringWithFormat:@"%02d.jpg",i%10+1];
            
            //1.URL
            NSURL *url =[[NSBundle mainBundle] URLForResource:fileName withExtension:nil];
            
            //2.去Bundle里面加载我们的图片二进制数据
            NSData *data =[NSData dataWithContentsOfURL:url];
            
            //3.将图片的二进制数据,转成UIImage对象
            UIImage *image = [UIImage imageWithData:data];
            
            
            //4.将我们的图像添加到photoList数组中去
            NSLog(@"%@----%d",[NSThread currentThread],i);
            
            dispatch_barrier_async(_concurrentQueue, ^{
                [self.photoList addObject:image];
            });
        });
    }

    扩展:

    如果想在dispatch_queue中所有的任务执行完成后在做某种操作,在串行队列中,可以把该操作放到最后一个任务执行完成后继续,但是在并行队列中怎么做呢。这就有dispatch_group 成组操作。

    dispatch_queue_t dispatchQueue = dispatch_queue_create("ted.queue.next", DISPATCH_QUEUE_CONCURRENT);
    dispatch_group_t dispatchGroup = dispatch_group_create();
    dispatch_group_async(dispatchGroup, dispatchQueue, ^(){
    NSLog(@"dispatch-1");
    });
    dispatch_group_async(dispatchGroup, dispatchQueue, ^(){
    NSLog(@"dspatch-2");
    });
    dispatch_group_notify(dispatchGroup, dispatch_get_main_queue(), ^(){
    NSLog(@"end");
    });
    

      

    上面的 log1 和log2输出顺序不定,因为是在并行队列上执行,当并行队列全部执行完成后,最后到main队列上执行一个操作,保证“end”是最后输出。 另外,这里也可以不用创建自己的并行队列,用全局的global,那个也是个并行队列. dispatch_get_gloable_queue(0,0);

    常用方式3:单例(dispatch_once)的创建和延迟执行(dispatch_after)

    Tips:

    在NSOperationQueue中,我们可以随时取消已经设定要准备执行的任务(当然,已经开始的任务就无法阻止了),而GCD没法停止已经加入queue的block(其实是有的,但需要许多复杂的代码);
    GCD原生并不支持取消操作。

    dispatch_suspend函数也只能暂停开启新的未执行的block,已经处于执行中的block是无法暂停的。

    • NSOperation(抽象类不能直接使用,方式一:使用NSInvocationOperation 和 NSBlockOperation,常用方式二:自定义NSOperation即写一个子类继承自它需要重写它的main方法就可以把子类的对象放入NSOperationQueue队列中直接使用了)和NSOperationQueue

    常见用法一:添加异步任务之间的依赖

    /**
        1.登录
        2.付费
        3.下载
        4.通知用户
     
        1.依赖的代码,必须放在添加任务前
        2.不要造成循环依赖,iOS7之前是直接崩,iOS8,不调度了
     */
    - (void)depencyDemo{
        NSLog(@"%s",__FUNCTION__);
        
        NSBlockOperation *login = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"%@----login",[NSThread currentThread]);
        }];
        
        NSBlockOperation *notifyUser = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"%@----notifyUser",[NSThread currentThread]);
        }];
        
        NSBlockOperation *downLoad = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"%@----downLoad",[NSThread currentThread]);
        }];
        
        NSBlockOperation *pay = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"%@----pay",[NSThread currentThread]);
        }];
        
        NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];
        
        //GCD,同步,串行队列
        //NSOperation,addDependency就是表示我这个任务,要依赖于哪个任务执行完
        [downLoad addDependency:pay];
        [pay addDependency:login];
        [notifyUser addDependency:downLoad];
        //[login addDependency:downLoad];//???
        
        //将任务添加到`队列`中去
    //    [mainQueue addOperation:login];
    //    [mainQueue addOperation:notifyUser];
    //    [self.concurrentQueue addOperation:downLoad];
    //    [mainQueue addOperation:pay];
        // 注意download是放在异步线程里的
         [self.concurrentQueue addOperation:downLoad];
        
        //YES,同步,       主队列千万不要和同步搞在一起
        [mainQueue addOperations:@[login,notifyUser,pay] waitUntilFinished:NO];
        
    }

    打印结果如下:

    [ViewController depencyDemo]
    <NSThread: 0x7fa88a707320>{number = 1, name = main}----login
    <NSThread: 0x7fa88a707320>{number = 1, name = main}----pay
    <NSThread: 0x7fa88a4bf850>{number = 2, name = (null)}----downLoad
    <NSThread: 0x7fa88a707320>{number = 1, name = main}----notifyUser

    用法二:支持KVO可以观察操作状态(正在执行、是否结束、是否取消)

    用法三:支持设置最大并发数(省电)而GCD不可以

    用法四:异步耗时操作主队列更新UI

        //1.创建一个并发队列
        NSOperationQueue *queue = [[NSOperationQueue alloc] init];
        
        //2.创建任务,并将其添加到`并发队列中`
        [queue addOperationWithBlock:^{
            NSLog(@"login===>%@",[NSThread currentThread]);
            
            [NSThread sleepForTimeInterval:2.0];
            
            //去主线程更新UI
            [[NSOperationQueue mainQueue] addOperationWithBlock:^{
                [NSThread sleepForTimeInterval:3.0];
                //更新UI的代码
                NSLog(@"%@===>",[NSThread currentThread]);
            }];
        }];

    有新的发现会继续来完善....

  • 相关阅读:
    学用MVC4做网站六后台管理:6.1.3管理员修改密码
    学用MVC4做网站六后台管理:6.1.1管理员登录、6.1.2退出
    学用MVC4做网站六后台管理:6.1管理员(续)
    学用MVC4做网站六:后台管理(续)
    SiteCore Experience Analytics-路径分析地图
    sitecore教程路径分析器
    SiteCore Experience Analytics-体验分析
    sitecore系列教程之更改您的个人设置
    sitecore中的两种编辑工具介绍
    Sitecore CMS中如何管理默认字段值
  • 原文地址:https://www.cnblogs.com/zhaoyunboy/p/how-to-use-gcd-nsoperation.html
Copyright © 2011-2022 走看看