iOS - 多线程(二) GCD讲解

目录:

1.GCD简介

2.串行队列 + 同步执行

3.串行队列 + 异步执行

4.并发队列 + 同步执行

5.并发队列 + 异步执行

6.全局队列

7.主队列

8.线程间通信

9.GCD栅栏函数(dispatch_barrier_async)

10.GCD调度组

一.GCD简介

注意:开启线程的操作是CPU做的,GCD只负责任务的调度。


(1).GCD中涉及到两个十分重要的概念, 就是任务队列.

任务(Task): 你需要执行的操作;

队列(Queue): 存放任务的容器。


(2).GCD使用步骤,2步。

      定制任务(确定想要做的事情);

      然后将任务添加到队列中( GCD会自动将队列中的任务取出来,放到对应的线程中执行。取任务遵循队列FIFO原则:先添加进队列的任务先被取出来)。


(3).GCD中的队列分2类

       并发队列:可以让多个任务并发(同时)执行(能够开启多个线程同时执行任务),并发功能只有在异步执行函数下有效。

       串行队列:任务只能在同一条线程中(可以是主线程,也可以是子线程)一个一个按顺序执行。


(4).任务执行分为同步执行和异步执行

        同步执行 :dispatch_sync(dispatch_queue_t  _Nonnull queue, ^(void)block),一个任务没有结束,就不会去执行下一个任务。同步执行函数无法开启新线程。

        异步执行: dispatch_async(dispatch_queue_t  _Nonnull queue, ^(void)block),不用等待任务执行完,就可以执行下一个任务。异步执行函数有能力开启新线程。


二.各种情况详细分析

 

     (1).串行队列 + 同步执行

         //1.队列 -- 串行 DISPATCH_QUEUE_SERIAL 串行 等价于NULL

         dispatch_queue_t q = dispatch_queue_create("CC_GCD", NULL);

        //2.先循环添加任务,同步执行任务

         for (int i = 0;  i < 10; i++) {

                dispatch_sync(q, ^{

                          NSLog(@"%@,%d",[NSThread currentThread],i);

           });

    分析:首先创建了一个串行队列,然后循环向队列中添加10个任务,调用GCD同步执行函数执行队列中的任务。 由于是同步执行所以不会开启新的线程,任务都添加在串行队列,所以这些打印会按顺序执行,且都在主线程中执行。


    (2).串行队列 + 异步执行

            //1.队列 -- 串行 DISPATCH_QUEUE_SERIAL 串行 等价于NULL

            dispatch_queue_t q = dispatch_queue_create("CC_GCD", NULL);

          //2..先循环添加任务,异步执行任务

           for (int i = 0;  i < 10; i++) {

                 NSLog(@"%d------------- ",i);

                 dispatch_async(q, ^{

                  NSLog(@"%@,%d",[NSThread currentThread],i);

                 });

           }

          NSLog(@"come here");

分析:首先创建了一个串行队列,然后循环向队列中添加10个任务,调用GCD异步执行函数执行队列中的任务。 由于是异步执行所以会开启新的线程,任务都添加在串行队列,所以这些打印会按顺序执行,但是是在新的线程中执行,子线程只会开启1条。



    (3).并发队列 + 同步执行

               //1.队列 DISPATCH_QUEUE_CONCURRENT

              dispatch_queue_t q = dispatch_queue_create("CC_GCD", DISPATCH_QUEUE_CONCURRENT);

             //2.同步执行任务

             for (int i = 0;  i < 10; i++) {

                    NSLog(@"%d------------- ",i);

                    dispatch_sync(q, ^{

                             NSLog(@"%@,%d",[NSThread currentThread],i);

                    });

              }

              NSLog(@"come here");

分析:首先创建了一个并发队列,然后循环向队列中添加10个任务,调用GCD同步执行函数执行队列中的任务。 由于是同步执行所以不会开启新的线程,任务都添加在并发队列,虽然我们说并发队列里面的任务可以同时取出来执行,但是这里是同步执行,没有开启新的线程,所以这些打印还是会按顺序执行,且都在主线程中执行。



    (4).并发队列 + 异步执行

                //1.队列 DISPATCH_QUEUE_CONCURRENT

                dispatch_queue_t q = dispatch_queue_create("CC_GCD", DISPATCH_QUEUE_CONCURRENT);

               //2.异步执行任务

               for (int i = 0;  i < 10; i++) {

                       NSLog(@"%d------------- ",i);

                      dispatch_async(q, ^{

                               NSLog(@"%@,%d",[NSThread currentThread],i);

                      });

                }

                NSLog(@"come here");

分析:首先创建了一个并发队列,然后循环向队列中添加10个任务,调用GCD异步执行函数执行队列中的任务。 由于是异步执行所以会开启新的线程,任务都添加在并发队列,所以这十个任务能够同时执行,那么久会开启多条线程,至于开启几条线程用CPU决定,程序员无法决定。



  (5).全局队列(本质上是一个并发队列)

       创建全局队列函数:dispatch_get_global_queue(long identifier, unsigned long flags);

      参数说明:    参数一:涉及系统适配

                                       iOS 8.0 服务质量

                                       QOS_CLASS_USER_INTERACTIVE  用户交互(希望线程快速执行,不要放一些耗时操作)

                                       QOS_CLASS_USER_INITIATED    用户需要的(不要放一些耗时操作)

                                       QOS_CLASS_DEFAULT          0 默认

                                       QOS_CLASS_UTILITY          使用工具(用来耗时操作)

                                       QOS_CLASS_BACKGROUND        后台

                                       QOS_CLASS_UNSPECIFIED      没有指定优先级

                                       iOS 7.0 调度优先级

                                       DISPATCH_QUEUE_PRIORITY_HIGH      高优先级

                                       DISPATCH_QUEUE_PRIORITY_DEFAULT 0 默认优先级

                                       DISPATCH_QUEUE_PRIORITY_LOW (-2)  低优先级

                                       DISPATCH_QUEUE_PRIORITY_BACKGROUND 后台优先级

                          参数二:为未来的保留参数,

                                       提示:尤其不要选择BACKGROUND  ,线程执行会慢到令人发指!



(6).主队列(本质上是一个并发队列)

        创建主队列函数:dispatch_get_main_queue(void)

        注:主队列上的任务只能够在主线程上执行,所以无论同步执行还是异步执行函数,都不会开启新线程。

        主队列+同步执行

       NSLog(@"------start-------");

       dispatch_sync(dispatch_get_main_queue(), ^{

                   NSLog(@"download1-------%@",[NSThread currentThread]);

       });

       dispatch_sync(dispatch_get_main_queue(), ^{

                   NSLog(@"download1-------%@",[NSThread currentThread]);

         });

       dispatch_sync(dispatch_get_main_queue(), ^{

                   NSLog(@"download1-------%@",[NSThread currentThread]);

       });

      NSLog(@"-------end---------");

分析:现在是直接崩溃了, 以前这种情况是会发生死锁的。

    

         主队列+异步函数

         // 1.获得主队列

        dispatch_queue_t queue = dispatch_get_main_queue();

        NSLog(@"-----start------");

      // 2.将任务封装到异步函数中 ,添加任务到主队列中

      dispatch_async(queue, ^{

               NSLog(@"download1 ----- %@", [NSThread currentThread]);

       });

       dispatch_async(queue, ^{

              NSLog(@"download2 ----- %@", [NSThread currentThread]);

      });

      dispatch_async(queue, ^{

      NSLog(@"download3 ----- %@", [NSThread currentThread]);

      });

     NSLog(@"-----end------");

分析:不开线程,start和end执行完后,主队列中的任务串行在主线程执行。



(7).  线程间通信


     // 1. 创建子线程处理耗时操作

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

                   //1.1耗时操作开始

                  for (int i = 0; i < 10000; i++) {

                        NSLog(@"---耗时操作-%@----%d", [NSThread currentThread],i);

                 }

               //1.2耗时操作完成回到主线程中

              dispatch_async(dispatch_get_main_queue(), ^{

                        NSLog(@"----%@----", [NSThread currentThread]);

             });

});



(8)GCD栅栏函数(dispatch_barrier_async)

           dispatch_barrier_async函数的作用与barrier的意思相同,在进程管理中起到一个栅栏的作用,它等待所有位于barrier函数之前的操作执行完毕后执行,并且在barrier函数执行之后,barrier函数之后的操作才会得到执行,该函数需要同dispatch_queue_create函数生成的concurrent Dispatch Queue队列一起使用。

例:

栅栏函数使用示例代码

输出结果:

分析:前十个 --> barrier -->后十个  其中 前十个 与 后十个 由于并行处理先后顺序不定

运用场景:用barrier实现: 读-写锁,读-写锁有以下几个特点:  在没有写操作的时候, 可以任意的并发读取, 在所有读操作完成后, 才进行写操作, 但是写操作不可以并发, 且在写操作过程中, 不能读取, 在写操作完成后, 又可以任意的并发读取了。如果我们使用barrier GCD接口来处理写操作, 使用普通的GCD接口来并发读取, 那么完全满足读-写锁的以上特点

dispatch_barrier_sync和dispatch_barrier_async的区别:

在将任务插入到queue的时候,dispatch_barrier_sync需要等待自己的任务结束之后才会继续程序,然后插入被写在它后面的任务,然后执行后面的任务

而dispatch_barrier_async将自己的任务插入到queue之后,不会等待自己的任务结束,它会继续把后面的任务插入到queue

所以,dispatch_barrier_async的不等待(异步)特性体现在将任务插入队列的过程,它的等待特性体现在任务真正执行的过程。


(9).GCD调度组

使用场景:在实际开发中,需要开启N个异步线程,但是后续操作,需要依赖N个线程返回的数据,需要接收所有线程任务执行完成的通知。

例:

#pragma mark - 调度组

- (void)group{

/*

*应用场景:

开发的时候,有时候出现多个网络请求都完成以后(每个网络请求时间的时长不一样),再通知用户

比如下载书:三国演义,红楼梦,西游记

*/

          //1.实例化一个调度组

        dispatch_group_t group = dispatch_group_create();

       //2.队列

       dispatch_queue_t queue = dispatch_get_global_queue(0, 0);

      //3.任务添加到队列queue

     dispatch_group_async(group,  queue, ^{

           for (int i = 0; i<=50; i++) {

                  if (i == 50) {

                      NSLog(@"---下载小说A:%@",[NSThread currentThread]);

                  }

          }

   });

  dispatch_group_async(group,  queue, ^{

         for (int i = 0; i<=50; i++) {

                if (i == 50) {

                    NSLog(@"---下载小说B:%@",[NSThread currentThread]);

              }

    }

});

dispatch_group_async(group,  queue, ^{

         for (int i = 0; i<=500; i++) {

               if (i == 50) {

                    NSLog(@"---下载小说C:%@",[NSThread currentThread]);

             }

        }

});

//4.获得所有调度组里面的异步任务完成的通知<上面3本书都下载完了才会执行>

    dispatch_group_notify(group, queue, ^{

           NSLog(@"--- 下载完成的操作:%@",[NSThread currentThread]);

  });

}

输出结果:

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,185评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,445评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,684评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,564评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,681评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,874评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,025评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,761评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,217评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,545评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,694评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,351评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,988评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,778评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,007评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,427评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,580评论 2 349

推荐阅读更多精彩内容