zoukankan      html  css  js  c++  java
  • 多线程编程-002-GCD

     

    什么是GCD

     

    全称是Grand Central Dispatch,可译为“牛逼的中央调度器”,纯C语言,提供了非常多强大的函数

    GCD的优势

    GCD是苹果公司为多核的并行运算提出的解决方案

    GCD会自动利用更多的CPU内核(比如双核、四核)

    GCD会自动管理线程的生命周期(创建线程、调度任务、销毁线程)

    程序员只需要告诉GCD想要执行什么任务,不需要编写任何线程管理代码

     

    GCD2个核心概念

     

    1>任务:执行什么操作

    2>队列:用来存放任务

    GCD使用步骤

    //1.定制任务
    确定想做的事情
    //2.将任务添加到队列中
    GCD会自动将队列中的任务取出,放到对应的线程中执行
    //任务的取出遵循队列的FIFO原则:先进先出,后进后出

    程序员要做的是将任务添加到队列,队列按照程序员指定的方式调度任务

    ③同步/异步-并发/串行

    同步:一个人没有结束,就不会执行下一个任务

    异步:不用等待任务执行完毕,就会执行下一个任务

    并发和串行主要影响:任务的执行方式

     

    并发:允许多个任务同时执行

    串行:一个任务执行完毕后,再执行下一个任务

    1>同步执行(sync这一句不执行,就不会执行下一句)

    dispatch_sync(dsipatch_queue_t queue, dispatch_block_t block);//queue 队列;block 任务

     //同步执行方法:(sync):这一句不执行完成,就不会执行下一个任务。同步线程是不会开启子线程
        //1.创建队列
        dispatch_queue_t q = dispatch_get_global_queue(0, 0);
        //2.将任务添加到队列中
        //2.1定义一个任务--block
        void (^tasks)(void) = ^{
            NSLog(@"%@",[NSThread currentThread]);//主线程
        };
        //2.2添加任务到队列,并且会执行
        dispatch_sync(q, tasks);

    2>异步执行(如果任务没有完成,可以不等待,异步执行下一个任务[具备开启线程的能力])

    dispatch_async(dispatch_queue_t queue, dispatch_block_t block);

        //异步执行任务 如果任务没有执行完毕,可以不要等待,异步执行下一个任务。具备开启线程的能力。异步通常是多线程的代名词;
        //1.创建队列 并发队列
        dispatch_queue_t q = dispatch_get_global_queue(0, 0);
        //2.将任务添加到队列中
        //2.1定义一个任务--block
        void (^tasks)(void) = ^{
            NSLog(@"%@",[NSThread currentThread]);//子线程
        };
        //3添加任务到队列
        dispatch_async(q, tasks);

    同步和异步的区别:

    //同步:只能在当前线程中执行任务,不具备开启新线程的能力
    //异步:可以在新的线程中执行任务,具备开启新线程的能力

    线程间通信

        //1指定任务的执行方法--异步
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            //耗时操作
            NSLog(@"%@",[NSThread currentThread]);
            //更新UI
            dispatch_async(dispatch_get_main_queue(), ^{
                NSLog(@"更新UI%@",[NSThread currentThread]);
            });
        });

    利用GCD来加载图片

    dispatch_async(dispatch_get_global_queue(0, 0), ^{
            NSURL * url = [NSURL URLWithString:@"https://a-ssl.duitang.com/uploads/item/201503/09/20150309162548_LnTA3.thumb.700_0.jpeg"];
            NSData * data = [NSData dataWithContentsOfURL:url];
            UIImage * image = [UIImage imageWithData:data];
            //更新UI
            dispatch_async(dispatch_get_main_queue(), ^{
                self.imageView.image = image;
                [self.imageView sizeToFit];
                self.scrollView.contentSize = image.size;
            });
        });

    从子线程回到主线程

    dispatch_async(
    dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        // 执行耗时的异步操作...
          dispatch_async(dispatch_get_main_queue(), ^{
            // 回到主线程,执行UI刷新操作
            });
    });

    队列

    //-串行队列:一个接着一个地执行任务
    //-并发队列:可以同时调度多个任务
    //-任务执行函数(任务都需要在线程中执行)
    //-同步执行:当前的任务不完成,不会执行下一个任务
    //-异步执行:当前任务不完成,同样执行下一个任务
    //-同步执行:不会到线程池里面去获取子线程
    //-异步执行:只要有任务,就会到线程池里面去获取子线程(主队列除外)
     小结:
    // -- 开不开线程取决于执行任务的函数,同步不开,异步开;
    // -- 开几条线程,取决于队列,串行开一条,并发开多条(异步)由CPU决定条数

     

    队列的类型

     

    1->并发队列(Concurrent Dispatch Queue)

    可以让多个任务并发(同时)执行(自动开启多个线程同时执行任务)

    并发功能只有在异步(dispatch_async)函数下才有效

    使用dispatch_queue_create 函数创建队列

    dispatch_queue_t
    dispatch_queue_create(const char *label, // 队列名称 
    dispatch_queue_attr_t attr); // 队列的类型

    dispatch_get_global_queue 获取全局的并发队列

    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 
    dispatch_queue_t queue = dispatch_queue_create("com.520it.queue", DISPATCH_QUEUE_CONCURRENT);
    GCD默认已经提供了全局的并发队列,供整个应用使用,可以无需手动创建
    使用dispatch_get_global_queue函数获得全局的并发队列
    dispatch_queue_t dispatch_get_global_queue(
    dispatch_queue_priority_t priority, // 队列的优先级
    unsigned long flags); // 此参数暂时无用,用0即可

    全局并发队列的优先级

    #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_queue_t q = dispatch_queue_create("CC_GCD8", DISPATCH_QUEUE_CONCURRENT);
        for (int i = 0; i<10; i++) {
            dispatch_async(q, ^{
                NSLog(@"%@ %d",[NSThread currentThread],i);
            });
        }
    /*
    电量优化、流量优化
          全局队列&串行队列:
             全局队列:并发,能够调度多个线程,执行效率高-耗电!
             串行队列:一个一个执行,执行效率低 - 省电
          判断依据:根据用户的上网方式
         Wifi:可以多开线程!
         流量:尽量少开线程
         几个典型的耗电场景:
            1.定位,GPS定位
            2.网络传输,在非Wifi环境
            3.CPU频率
            4.内存调度频率
            5.后台运行!
    */

    2>串行队列(Serial Dispatch Queue)

    让任务一个接着一个地执行(一个任务执行完毕后,再执行下一个任务)

    GCD中获得串行有2种途径

    1>>使用dispatch_queue_create函数创建串行队列

    // 创建串行队列(队列类型传递NULL或者DISPATCH_QUEUE_SERIAL)
    dispatch_queue_t queue = dispatch_queue_create("com.520it.queue", NULL); 

    2>>使用主队列(跟主线程相关联的队列)

    //主队列是GCD自带的一种特殊的串行队列 放在主队列中的任务
    //都会放到主线程中执行
    //使用dispatch_get_main_queue()获得主队列
    dispatch_queue_t queue = dispatch_get_main_queue();

    3->GCD调度组 dispatch_group_t

        //1.队列
        dispatch_queue_t q = dispatch_get_global_queue(0, 0);
        //2.调度组
        dispatch_group_t g = dispatch_group_create();
        //3.添加任务,将队列调度任务。任务完成的时候,能够提醒
        dispatch_group_async(g, q, ^{
            NSLog(@"download A %@",[NSThread currentThread]);
        });
        dispatch_group_async(g, q, ^{
            [NSThread sleepForTimeInterval:1.0];
            NSLog(@"download B %@",[NSThread currentThread]);
        });
        dispatch_group_async(g, q, ^{
            [NSThread sleepForTimeInterval:1.0];
            NSLog(@"download C %@",[NSThread currentThread]);
        });
        
        //4.当所有任务执行完毕后,通知
    //    dispatch_group_notify(g, q, ^{
    //        NSLog(@"OK %@",[NSThread currentThread]);
    //    });
        dispatch_group_notify(g, dispatch_get_main_queue(), ^{
            //更新UI,通知用户
            NSLog(@"OK %@",[NSThread currentThread]);//1
        });
        NSLog(@"Come Here ");

    dispatch_barrier_async(dispatch_queue_t queue, dispatch_block_t block);

    这个函数要等在前面的任务执行结束后它才执行,而且它后面的任务等它执行完成之后才会执行。(这个queue不能是全局的并发队列)

    ⑥案例

    1>主线程同步死锁dispatch_sync

        //主队列: 专门负责在主线程上调度任务的一个队列!...>>不会开线程
        //1.队列
        dispatch_queue_t t = dispatch_get_main_queue();
        //dispatch_sync 一个接一个造成死锁 会崩溃
        //同步任务
        dispatch_sync(t, ^{
            NSLog(@"%@",[NSThread currentThread]);
        });
        NSLog(@"COme here");

    2>主线程同步不死锁dispatch_async

      void (^task)(void) = ^{
            NSLog(@"这里 !!! %@",[NSThread currentThread]);
            //1.队列
            dispatch_queue_t t = dispatch_get_main_queue();
            //dispatch_sync 一个接一个造成死锁 会崩溃
            //同步任务
            dispatch_sync(t, ^{
                NSLog(@"%@",[NSThread currentThread]);
            });
        };
        dispatch_async(dispatch_get_global_queue(0, 0), task);

    3>

    //MARK:串行队列,同步执行
    会不会开辟线程?不能【串并行不能决定是否开启子线程;异步才可以开启子线程】顺序执行吗?是
    //MARK:串行队列,异步执行 
    串行 决定怎么拿任务 会不会开辟线程?会顺序执行吗?会 是
    //MARK: 并发队列,异步执行:
    //会不会开辟线程?  顺序执行吗?   comeHere
    //会            不是         不确定
    //MARK: 并发队列,同步执行:
    //会不会开辟线程?  顺序执行吗?   comeHere
    //不会            是           最后
    //MARK: 同步任务加强
    在开发中,通常耗时操作放在后台执行,有的时候,任务之间有依赖关系 例如:登录》支付、下载
    利用同步任务,能做到任务依赖关系,前一个任务是同步任务,不执行完,队列就不会调度后面的任务
    //MARK:增强版同步任务
    队列调度多个任务之前,指定一个同步任务,让所有的异步,等待同步任务执行完成,就是依赖关系。同步任务就相当于一个锁

     

     

     

     

     

     

     

     

     

     

     

     

     

  • 相关阅读:
    hive、sqoop、MySQL间的数据传递
    centos7配置Hadoop集群环境
    crontab定时时间解释
    Jmeter小技巧以及问题集合
    【总结】梳理下接口功能测试
    【部署问题】解决Nginx: [error] open() "/usr/local/Nginx/logs/Nginx.pid" failed(2:No such file or directory)
    【C#公共帮助类】 ToolsHelper帮助类
    【C#公共帮助类】枚举独特类
    【无私分享:从入门到精通ASP.NET MVC】从0开始,一起搭框架、做项目(4)对前面的一些问题汇总和总结
    【C#公共帮助类】分页逻辑处理类
  • 原文地址:https://www.cnblogs.com/StevenHuSir/p/iOS_GCD.html
Copyright © 2011-2022 走看看