一、GCD
  • GCD:Grand Central Dispatch,宏大的中央调度!!!是苹果为多核的并行运算提出的解决方案,会自动合理地利用更多的CPU内核(比如双核、四核), GCD的底层依然是用线程实现,但是它会自动管理线程的生命周期(创建线程、调度任务、销毁线程),完全不需要我们管理,我们只需要告诉干什么就行。同时它使用的也是 C语言,不过由于使用了 Block(Swift里叫做闭包),使得使用起来更加方便,而且灵活。
 
二、GCD的两个概念:任务和队列
  • 任务:即操作,就是一段代码,在 GCD 中就是一个 Block。任务有两种执行方式: 同步执行 和 异步执行,他们之间的区别:是否会创建新的线程。

    • 同步执行:只要是同步执行的任务,都会在当前线程执行,不会另开线程。
    • 异步执行:只要是异步执行的任务,都会另开线程,在别的线程执行。
    • 同步(sync) 和 异步(async) 的主要区别在于会不会阻塞当前线程,直到 Block 中的任务执行完毕!
    • 同步(sync) 操作,会阻塞当前线程并等待 Block 中的任务执行完毕,然后当前线程才会继续往下运行。
    • 异步(async)操作,当前线程会直接往下执行,它不会阻塞当前线程。
   
  • 队列:用于存放任务。一共有两种队列,

    • 串行队列:串行队列中的任务会根据队列的定义 FIFO 的执行,一个接一个的先进先出的进行执行。
    • 并行队列:并行队列中的任务根据同步或异步有不同的执行方式。
      • 放到串行队列的任务,GCD 也会 FIFO的取出来,但不同的是,它取出来一个就会放到别的线程,然后再取出来一个又放到另一个的线程。这样由于取的动作很快,忽略不计,看起来,所有的任务都是一起执行的。不过需要注意,GCD 会根据系统资源控制并行的数量,所以如果任务很多,它并不会让所有任务同时执行。
         
三、创建队列
  • 主队列:这是一个特殊的 串行队列。它用于刷新 UI,任何需要刷新UI的工作都要在主队列执行,所以一般耗时的任务都要放到别的线程执行。

    • dispatch_queue_t queue = dispatch_get_main_queue();
  • 自己创建的队列:自己可以创建 串行队列, 也可以创建 并行队列。
    • 它有两个参数,第一个参数是标识符,用于 DEBUG 的时候标识唯一的队列,可以为空。
    • 第二个才是最重要的,用来表示创建的队列是串行的还是并行的,
      • 传入 DISPATCH_QUEUE_SERIAL 或 NULL 表示创建串行队列,通常用于同步访问特定的资源或数据。当你创建多个Serial queue时,虽然它们各自是同步执行的,但Serial queue与Serial queue之间是并发执行的。
      • 传入 DISPATCH_QUEUE_CONCURRENT 表示创建并行队列,可以并发地执行多个任务,但是执行完成的顺序是随机的。
    • dispatch_queue_t queue = dispatch_queue_create("tk.bourne.testQueue", NULL);
  • 全局并行队列:这应该是唯一一个并行队列,只要是并行任务一般都加入到这个队列。
    • dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    • 优先级(第一个参数):
      • #define DISPATCH_QUEUE_PRIORITY_HIGH 2 //优先级最高,在default,和low之前执行
        #define DISPATCH_QUEUE_PRIORITY_DEFAULT 0 // 默认优先级,在low之前,在high之后
        #define DISPATCH_QUEUE_PRIORITY_LOW (-2) //在high和default后执行
        #define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN  //提交到这个队列的任务会在high优先级的任务和已经提交到background队列的执行完后执行。
四、创建任务
  • 同步任务:不会另开线程 (SYNC)

    •  dispatch_sync(, ^{
            //code here
            NSLog(@"%@", [NSThread currentThread]);
        });
  • 异步任务:会另开线程 (ASYNC)
    •  dispatch_async(, ^{
            //code here
            NSLog(@"%@", [NSThread currentThread]);
        });
  • 示例一:
    • - (void)gcdTest{
          NSLog(@"之前 - %@ ", NSThread.currentThread);
          dispatch_sync(dispatch_get_main_queue(), ^{
              NSLog(@"sync - %@", NSThread.currentThread);
          });
          NSLog(@"之后 - %@", NSThread.currentThread);
      }
    • // 只会打印第一句:之前 - {number = 1, name = main} ,然后主线程就卡死了
    • 原因:同步任务会阻塞当前线程,然后把 Block 中的任务放到指定的队列中执行,只有等到 Block 中的任务完成后才会让当前线程继续往下运行。
    • 打印完第一句后,dispatch_sync 立即阻塞当前的主线程,然后把 Block 中的任务放到 main_queue 中,可以 main_queue 中的任务会被取出来放到主线程中执行,但主线程这个时候已经被阻塞了,所以 Block 中的任务就不能完成,它不完成,dispatch_sync 就会一直阻塞主线程,这就是死锁现象。导致主线程一直卡死。
  • 示例二:
    • - (void)gcdTe1 {
          dispatch_queue_t queue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_SERIAL);
          NSLog(@“ 1 之前 - %@", NSThread.currentThread);
          dispatch_async(queue, ^{
              NSLog(@“ 2 同步之前 - %@", NSThread.currentThread);
              dispatch_sync(queue, ^{
                 NSLog(@“ 3 同步 - %@", NSThread.currentThread);
              });
              NSLog(@“ 4 同步之后 - %@", NSThread.currentThread);
          });
          NSLog(@“ 5 之后 - %@", NSThread.currentThread);
      }
    • 1,2,5会打印,3,4不会
    • dispatch_async 异步执行,当前线程不会被阻塞,于是有了两条线程,一条当前线程继续往下打印出 1 , 另一条执行 Block 中的内容打印 2。因为这两条是并行的,所以打印的先后顺序无所谓。
      dispatch_sync同步执行,它所在的线程会被阻塞,一直等到 sync 里的任务执行完才会继续往下。于是 sync 就把自己 Block 中的任务放到 queue 中,可 queue 是一个串行队列,一次执行一个任务,所以 sync 的 Block 必须等到前一个任务执行完毕,但是 queue 正在执行的任务就是被 sync 阻塞了的那个。于是发生了死锁。所以 sync 所在的线程被卡死了。
  • 队列组
    • 可以将很多队列添加到一个组里,当这个组里所有的任务都执行完了,队列组会通过 dispatch_group_notify方法通知我们。
    • 示例:
      • - (void)group1 {
            dispatch_group_t group = dispatch_group_create();
            dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
            //多次使用队列组的方法执行任务, 只有异步方法
            dispatch_group_async(group, queue, ^{
                for (NSInteger i = 0; i < 3; i++) {
                    NSLog(@"group-01 - %@", [NSThread currentThread]);
                }
            });
            
            dispatch_group_async(group, dispatch_get_main_queue(), ^{
                for (NSInteger i = 0; i < 8; i++) {
                    NSLog(@"group-02 - %@", [NSThread currentThread]);
                }
            });

        dispatch_group_async(group, queue, ^{
                for (NSInteger i = 0; i < 5; i++) {
                    NSLog(@"group-03 - %@", [NSThread currentThread]);
                }
            });
            //都完成后会自动通知
            dispatch_group_notify(group, dispatch_get_main_queue(), ^{
                NSLog(@"完成 - %@", [NSThread currentThread]);
            });
        }

五、单例模式
  • @interface Tool : NSObject
    + (instancetype)sharedTool;
    @end
    @implementation Tool
    static id _instance;
    + (instancetype)sharedTool {
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            _instance = [[Tool alloc] init];
        });
        return _instance;
    }
    @end
 

六、延迟执行
  • dispatch_after:延迟一段时间把一项任务提交到队列中执行,返回之后就不能取消
  • dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{   //code  });
    • delayInSeconds  延迟的时间
  • dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@"---->(∩_∩)O哈哈~”);  //延迟5秒
    });
  • 可以对它封装一下
    • - (void)myGcdAfterTime:(double)time codeBlock:(void (^)())codeBlock{
          dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(time * NSEC_PER_SEC)), dispatch_get_main_queue(), codeBlock);
      }
    • 使用:[selfmyGcdAfterTime:5.0codeBlock:^{
              NSLog(@"---->O(∩_∩)O哈哈~");
           }];
 
七、其他用法
  • 屏障
  • dispatch_barrier_async 是在前面的任务执行结束后它才执行,而且它后面的任务等它执行完成之后才会执行
    • 这个方法重点是你传入的 queue,当你传入的 queue 是通过 DISPATCH_QUEUE_CONCURRENT 参数自己创建的 queue 时,这个方法会阻塞这个 queue(注意是阻塞 queue ,而不是阻塞当前线程),一直等到这个 queue 中排在它前面的任务都执行完成后才会开始执行自己,自己执行完毕后,才会取消阻塞,使这个 queue 中排在它后面的任务继续执行。
    • 如果你传入的是其他的 queue, 那么它就和 dispatch_async 一样了
    • 示例:
      • - (void)gcdBarrier {
            dispatch_queue_t queue = dispatch_queue_create("gcdtest.wwl", DISPATCH_QUEUE_CONCURRENT);
            dispatch_async(queue, ^{
                [NSThread sleepForTimeInterval:2];
                NSLog(@"---> 1 ");
            });
            dispatch_async(queue, ^{
                [NSThread sleepForTimeInterval:4];
                NSLog(@"---> 2 ");
            });
            dispatch_barrier_async(queue, ^{
                NSLog(@"---> 3 ");
                [NSThread sleepForTimeInterval:4];
               
            });
            dispatch_async(queue, ^{
                [NSThread sleepForTimeInterval:1];
                NSLog(@"---> 4 ");
            });
        }
      • 注意打印时间
      • 13:38:15.526  ---> 1
         13:38:17.526  ---> 2
         13:38:17.527  ---> 3
         13:38:22.539  ---> 4
  • dispatch_barrier_sync
    • 这个方法的使用和上一个一样,传入 自定义的并发队列(DISPATCH_QUEUE_CONCURRENT),它和上一个方法一样的阻塞 queue,不同的是这个方法还会阻塞当前线程。
    • 如果你传入的是其他的 queue, 那么它就和 dispatch_sync 一样了。
 
  • dispatch_apply

    • 把一项任务提交到队列中多次执行,具体是并行执行还是串行执行由队列本身决定.注意,dispatch_apply不会立刻返回,在执行完毕后才会返回,是同步的调用。
  • dispatch_apply(size_t iterations, dispatch_queue_t queue, ^(size_t) {  //code  });
    • size_t iterations:代码执行次数
    • dispatch_queue_t queue:传入的队列
    • size_t:代码当前的执行次数,
  • 示例:
    • - (void)gcdApply {
          dispatch_apply(5, dispatch_get_global_queue(0, DISPATCH_QUEUE_PRIORITY_DEFAULT), ^(size_t index) {
               NSLog(@"----> %ld  %@",index, [NSThread currentThread]);
          });
      }
    • ----> 0   <NSThread: 0x16669b20>{number = 1, name = main}
      ----> 1   <NSThread: 0x16587ed0>{number = 2, name = (null)}
      ----> 3   <NSThread: 0x16587ed0>{number = 2, name = (null)}
      ----> 4   <NSThread: 0x16587ed0>{number = 2, name = (null)}
      ----> 2   <NSThread: 0x16669b20>{number = 1, name = main}
    • 因为传入的是全局队列,是并行的,所以会开线程,打印的顺序不确定。
    • 换成串行队列会循序打印,不开线程
    • dispatch_queue_t queue = dispatch_queue_create("gcdtest.wwl", DISPATCH_QUEUE_SERIAL);
          dispatch_apply(5, queue, ^(size_t index) {
              NSLog(@"----> %ld  %@",index,[NSThread currentThread]);
          });
  • 从其他线程回到主线程
    • dispatch_async(dispatch_get_main_queue(), ^{
              //注意是异步
          });
 
  • 下载图片示例:

    • - (void)downloadImages {
          dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
         
          // 异步下载图片
          dispatch_async(queue, ^{
              // 创建一个组
              dispatch_group_t group = dispatch_group_create();
             
              __blockUIImage *image1 = nil;
              __blockUIImage *image2 = nil;
             
              // 关联一个任务到group
              dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
                  // 下载第一张图片
                  NSString *url1 = @"http://car0.autoimg.cn/upload/spec/9579/u_20120110174805627264.jpg";
                  image1 = [selfimageWithURLString:url1];
              });
             
              // 关联一个任务到group
              dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
                  // 下载第一张图片
                  NSString *url2 = @"http://hiphotos.baidu.com/lvpics/pic/item/3a86813d1fa41768bba16746.jpg";
                  image2 = [selfimageWithURLString:url2];
              });
             
              // 等待组中的任务执行完毕,回到主线程执行block回调
              dispatch_group_notify(group, dispatch_get_main_queue(), ^{
                  self.imageView1.image = image1;
                  self.imageView2.image = image2;
              });
          });
      }

      - (UIImage *)imageWithURLString:(NSString *)urlString {
          NSURL *url = [NSURLURLWithString:urlString];
          NSData *data = [NSDatadataWithContentsOfURL:url];
          return [[UIImagealloc] initWithData:data];
      }

 
 

多线程(三)GCD的更多相关文章

  1. iOS 开发多线程篇—GCD的常见用法

    iOS开发多线程篇—GCD的常见用法 一.延迟执行 1.介绍 iOS常见的延时执行有2种方式 (1)调用NSObject的方法 [self performSelector:@selector(run) ...

  2. iOS开发多线程篇—GCD介绍

    iOS开发多线程篇—GCD介绍 一.简单介绍 1.什么是GCD? 全称是Grand Central Dispatch,可译为“牛逼的中枢调度器” 纯C语言,提供了非常多强大的函数 2.GCD的优势 G ...

  3. iOS开发多线程篇—GCD的基本使用

    iOS开发多线程篇—GCD的基本使用 一.主队列介绍 主队列:是和主线程相关联的队列,主队列是GCD自带的一种特殊的串行队列,放在主队列中得任务,都会放到主线程中执行. 提示:如果把任务放到主队列中进 ...

  4. iOS开发多线程篇—GCD的常见用法

    iOS开发多线程篇—GCD的常见用法 一.延迟执行 1.介绍 iOS常见的延时执行有2种方式 (1)调用NSObject的方法 [self performSelector:@selector(run) ...

  5. [iOS]多线程和GCD

    新博客wossoneri.com 进程和线程 进程 是指在系统中正在运行的一个应用程序. 每个进程之间是独立的,每个进程均运行在其专用且受保护的内存空间内. 比如同时打开QQ.Xcode,系统就会分别 ...

  6. iOS开发多线程篇—GCD简介

    iOS开发多线程篇—GCD介绍 一.简单介绍 1.什么是GCD? 全称是Grand Central Dispatch,可译为“牛逼的中枢调度器” 纯C语言,提供了非常多强大的函数 2.GCD的优势 G ...

  7. 多线程(三) java中线程的简单使用

    java中,启动线程通常是通过Thread或其子类通过调用start()方法启动. 常见使用线程有两种:实现Runnable接口和继承Thread.而继承Thread亦或使用TimerTask其底层依 ...

  8. java 多线程三

    java 多线程一 java 多线程二 java 多线程三 java 多线程四 注意到 java 多线程一 中 MyThread2 运行结果出现0.-1,那是因为在操作共享数据时没有加锁导致. 加锁的 ...

  9. swift 多线程及GCD

    1.基本概念 1)进程: 进程是指在系统中正在运行的一个应用程序.每个进程之间是独立的,每个进程运行在其专用且受保护的内存空间里.某进程内的线程在其它进程不可见 2)线程: 1个进程要执行任务,必须有 ...

  10. iOS中的多线程及GCD

    多线程中的一些概念 //任务:代码段  方法  线程就是执行这些任务 //NSThread类 创建线程 执行线程 [NSThread isMainThread]//判断是否是主线程 #import & ...

随机推荐

  1. android学习笔记32——资源

    Android应用资源 资源分类: 1.无法直接访问的原生资源,保存于asset目录下 2.可通过R资源清单类访问的资源,保存于res目录下 资源的类型以及存储方式 android要求在res目录下用 ...

  2. erlang接入远程shell

    两种方式 erl -name aaa@127.0.0.1 -setcookie erl -name bbb@127.0.0.1 -setcookie ctrl + g进入jcl模式 h查看帮助 r ' ...

  3. Linux从逻辑地址到物理地址

    转自:http://blog.chinaunix.net/uid-24774106-id-3427836.html 我们都知道,动态共享库里面的函数的共享的,这也是动态库的优势所在,就是节省内存.C ...

  4. C#全角和半角转换

    在计算机屏幕上,一个汉字要占两个英文字符的位置,人们把一个英文字符所占的位置称为"半角",相对地把一个汉字所占的位置称为"全角".在汉字输入时,系统提供&quo ...

  5. Apache HttpClient

    HpptClient特性 1. 基于标准.纯净的java语言.实现了Http1.0和Http1.1 2. 以可扩展的面向对象的结构实现了Http全部的方法(GET, POST, PUT, DELETE ...

  6. Codeforces Round #368 (Div. 2) D. Persistent Bookcase

    Persistent Bookcase Problem Description: Recently in school Alina has learned what are the persisten ...

  7. NeHe OpenGL教程 第二十八课:贝塞尔曲面

    转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...

  8. 张恭庆编《泛函分析讲义》第二章第2节 $Riesz$ 定理及其应用习题解答

    在本节中, $\scrH$ 均指 $Hilbert$ 空间. 1.在极大闭子空间的交的最佳逼近元 设 $f_1,f_2,\cdots,f_n$ 是 $\scrH$ 上的一组线性有界泛函, $$\bex ...

  9. 安装完CentOS 7 后必做的七件事[转]

    CentOS是最多人用来运行服务器的 Linux 版本,最新版本是 CentOS 7.当你兴趣勃勃地在一台主机或 VPS 上安装 CentOS 7 后,首要的工作肯定是加强它的安全性,以下列出的七件事 ...

  10. iOS开发之检查更新

    iOS设备检查更新版本: #pragma mark - 检查更新 - (void)checkUpdateWithAPPID:(NSString *)APPID { //获取当前应用版本号 NSDict ...