一、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. 一个 IT 青年北漂四年的感悟

    转载自:http://www.codeceo.com/article/it-man-beijing-4-years.html 工作这几年,每年都会有朋友离开北京,每次朋友跟我告别的时候总是让我有很多感 ...

  2. flash bulider 无法启动

    更新airsdk后无法启动,google下http://www.25kx.com/art/1826181有n种方法,怀疑是.metadata目录原因,可能是旧版的airsdk和新版的airsdk在.m ...

  3. JMM & synchronized概述(转)

    根据Java语言规范中的说明,JVM系统中存在一个主内存(Main Memory),Java中所有的变量存储在主内存中,对于所有的线程是共享的(相当于黑板,其他人都可以看到的).每个线程都有自己的工作 ...

  4. (C++) 基本面试题(整理)

    1.new.delete.malloc.free关系 new/delete是C++的运算符.new 调用构造函数用于动态申请内存,delete调用对象的析构函数,用于释放内存. malloc与free ...

  5. Exception error message with incorrect line number

    In Release mode the number in front of the exception is NOT the line of code. Instead it's an offset ...

  6. IOS Bugs5 linker command failed with exit code 1 (use -v to see invocation)

    Ld /Users/Rubert/Library/Developer/Xcode/DerivedData/OC_Language-emftyzftyvhdpuaxipddjmpnpvox/Build/ ...

  7. SG函数

    入门一: 首先来玩个游戏,引用杭电课件上的: (1) 玩家:2人:(2) 道具:23张扑克牌:(3) 规则:游戏双方轮流取牌:每人每次仅限于取1张.2张或3张牌:扑克牌取光,则游戏结束:最后取牌的一方 ...

  8. NeHe OpenGL教程 第十课:3D世界

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

  9. [物理学与PDEs]书中出现的向量公式汇总

    P 11 1. $\rot (\phi{\bf A})=\n \phi\times{\bf A}+\phi\ \rot{\bf A}$. 2. $-\lap {\bf A}=\rot\rot {\bf ...

  10. 转--一款漂亮实用的Android开源日期控件timessquare

    这个开源控件可以兼容到SDK8版本,可以自定义显示的年月日,以及时间范围,如图 如果我们只想显示两个月的日期选择区间: final Calendar month = Calendar.getInsta ...