美文网首页
iOS - 多线程(二) GCD讲解

iOS - 多线程(二) GCD讲解

作者: 伦伦子_f7b3 | 来源:发表于2017-08-27 21:19 被阅读24次

目录:

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

  });

}

输出结果:

相关文章

网友评论

      本文标题:iOS - 多线程(二) GCD讲解

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