简介
GCD本身是苹果公司为多核的并行运算提出的解决方案。GCD在工作时会自动利用更多的处理器核心,以充分利用更强大的机器。GCD是Grand Central Dispatch的简称,它是基于C语言的。如果使用GCD,完全由系统管理线程,我们不需要编写线程代码。只需定义想要执行的任务,然后添加到适当的调度队列(dispatch queue)。GCD会负责创建线程和调度你的任务,系统直接提供线程管理

GCD优势

GCD可用于多核的并行运算

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

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

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

任务和队列

GCD中的两个核心概念

任务:执行什么操作,在GCD就是一个Block,任务执行的两种方式:同步执行和异步执行。主要区别在于是否具备开启新线程的能力

同步执行(sync):只能在当前线程中执行任务,不具备开启新线程的能力

异步执行(async):可以在新的线程中执行任务,具备开启新线程的能力

队列:存放任务,队列是一种特殊的线性表,采用FIFO(先进先出)的原则,即新任务总是被插入到队列的末尾,而读取任务的时候总是从队列的头部开始读取。每读取一个任务,则从队列中释放一个任务。在GCD中有两种队列:串行队列和并行队列。

注意:

放到并行队列的任务,GCD 也会 FIFO的取出来,但不同的是,它取出来一个就会放到别的线程,然后再取出来一个又放到另一个的线程。这样由于取的动作很快,忽略不计,看起来,所有的任务都是一起执行的。不过需要注意,GCD 会根据系统资源控制并行的数量,所以如果任务很多,它并不会让所有任务同时执行。
 

并行队列(Concurrent Dispatch Queue):可以让多个任务并行(同时)执行(自动开启多个线程同时执行任务)

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

串行队列(Serial Dispatch Queue):让任务一个接着一个地执行(一个任务执行完毕后,再执行下一个任务)

GCD的使用步骤

1、创建一个队列(串行队列或并行队列)

2、将任务添加到队列中,然后系统就会根据任务类型执行任务(同步执行或异步执行)1. 队列的创建方法

可以使用dispatch_queue_create来创建对象,需要传入两个参数,第一个参数表示队列的唯一标识符,用于DEBUG,可为空;第二个参数用来识别是串行队列还是并行队列。DISPATCH_QUEUE_SERIAL表示串行队列,DISPATCH_QUEUE_CONCURRENT表示并行队列。

// 串行队列的创建方法

dispatch_queue_t queue= dispatch_queue_create("test.queue", DISPATCH_QUEUE_SERIAL);

// 并行队列的创建方法

dispatch_queue_t queue= dispatch_queue_create("test.queue", DISPATCH_QUEUE_CONCURRENT);

对于并行队列,还可以使用dispatch_get_global_queue来创建全局并行队列。GCD默认提供了全局的并行队列,需要传入两个参数。第一个参数表示队列优先级,一般用DISPATCH_QUEUE_PRIORITY_DEFAULT。第二个参数暂时没用,用0即可。

2. 任务的创建方法

// 同步执行任务创建方法
dispatch_sync(queue, ^{
NSLog(@"%@",[NSThread currentThread]); // 这里放任务代码
});
// 异步执行任务创建方法
dispatch_async(queue, ^{
NSLog(@"%@",[NSThread currentThread]); // 这里放任务代码
});
既然有两种队列,两种执行任务的方式,就有四种不同的组合方式
还有一种特殊的队列是主队列(串行队列),这样就有六种组合方式
  同步执行(sync) 异步执行(async)
并行队列 不会开启新的线程,在当前线程中任务一个接一个执行 启新的线程,所有线程并行执行任务
串行队列 不会开启新的线程,在当前线程中任务一个接一个执行 只开启一条新的线程,在新的线程中任务一个接一个执行
主队列 不会开启新的线程,在主线程中任务一个接一个执行 不会开启新的线程,在主线程中任务一个接一个执行

并行队列+同步执行

+(void)syncConcurrent

{

NSLog(@"syncConcurrent---begin");

dispatch_queue_t queue= dispatch_queue_create("test.queue", DISPATCH_QUEUE_CONCURRENT);

dispatch_sync(queue, ^{

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

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

}

});

dispatch_sync(queue, ^{

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

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

}

});

dispatch_sync(queue, ^{

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

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

}

});

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

}

执行结果

2016-03-13 19:03:42.539 多线程_Demo[3812:271447] syncConcurrent---begin


2016-03-13 19:03:42.539 多线程_Demo[3812:271447] 1------<NSThread: 0x61000007f040>{number = 1, name = main}


2016-03-13 19:03:42.539 多线程_Demo[3812:271447] 1------<NSThread: 0x61000007f040>{number = 1, name = main}


2016-03-13 19:03:42.539 多线程_Demo[3812:271447] 2------<NSThread: 0x61000007f040>{number = 1, name = main}


2016-03-13 19:03:42.540 多线程_Demo[3812:271447] 2------<NSThread: 0x61000007f040>{number = 1, name = main}


2016-03-13 19:03:42.540 多线程_Demo[3812:271447] 3------<NSThread: 0x61000007f040>{number = 1, name = main}


2016-03-13 19:03:42.540 多线程_Demo[3812:271447] 3------<NSThread: 0x61000007f040>{number = 1, name = main}


2016-03-13 19:03:42.540 多线程_Demo[3812:271447] syncConcurrent---end

从执行结果可以看出,所有任务都是在主线程中执行,由于只有一个线程,所以所有的任务只能一个一个执行

并行队列+异步执行

+(void)asyncConcurrent{

NSLog(@"asyncConcurrent---begin");

dispatch_queue_t queue= dispatch_queue_create("test.queue", DISPATCH_QUEUE_CONCURRENT);

dispatch_async(queue, ^{

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

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

}

});

dispatch_async(queue, ^{

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

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

}

});

dispatch_async(queue, ^{

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

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

}

});

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

}

执行结果

2016-03-13 19:06:22.064 多线程_Demo[3812:271447] asyncConcurrent---begin


2016-03-13 19:06:22.064 多线程_Demo[3812:271447] asyncConcurrent---end


2016-03-13 19:06:22.064 多线程_Demo[3812:271569] 1------<NSThread: 0x608000261600>{number = 3, name = (null)}


2016-03-13 19:06:22.064 多线程_Demo[3812:273918] 2------<NSThread: 0x60000026ae00>{number = 4, name = (null)}


2016-03-13 19:06:22.064 多线程_Demo[3812:273919] 3------<NSThread: 0x61800007cd80>{number = 5, name = (null)}


2016-03-13 19:06:22.064 多线程_Demo[3812:271569] 1------<NSThread: 0x608000261600>{number = 3, name = (null)}


2016-03-13 19:06:22.064 多线程_Demo[3812:273918] 2------<NSThread: 0x60000026ae00>{number = 4, name = (null)}


2016-03-13 19:06:22.064 多线程_Demo[3812:273919] 3------<NSThread: 0x61800007cd80>{number = 5, name = (null)}

从执行结果可以看出,除了主线程,有开启了3个新的线程,所有的线程并行执行任务

串行队列+同步执行

+ (void) syncSerial

{

NSLog(@"syncSerial---begin");

dispatch_queue_t queue = dispatch_queue_create("test.queue", DISPATCH_QUEUE_SERIAL);

dispatch_sync(queue, ^{

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

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

}

});

dispatch_sync(queue, ^{

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

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

}

});

dispatch_sync(queue, ^{

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

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

}

});

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

}

执行结果

2016-03-13 19:07:11.269 多线程_Demo[3812:271447] syncSerial---begin


2016-03-13 19:07:11.269 多线程_Demo[3812:271447] 1------<NSThread: 0x61000007f040>{number = 1, name = main}


2016-03-13 19:07:11.269 多线程_Demo[3812:271447] 1------<NSThread: 0x61000007f040>{number = 1, name = main}


2016-03-13 19:07:11.270 多线程_Demo[3812:271447] 2------<NSThread: 0x61000007f040>{number = 1, name = main}


2016-03-13 19:07:11.270 多线程_Demo[3812:271447] 2------<NSThread: 0x61000007f040>{number = 1, name = main}


2016-03-13 19:07:11.270 多线程_Demo[3812:271447] 3------<NSThread: 0x61000007f040>{number = 1, name = main}


2016-03-13 19:07:11.270 多线程_Demo[3812:271447] 3------<NSThread: 0x61000007f040>{number = 1, name = main}


2016-03-13 19:07:11.270 多线程_Demo[3812:271447] syncSerial---end

从执行结果可以看出,所有任务都是在主线程中执行的,并没有开启新的线程。而且由于串行队列,所以按顺序一个一个执行

串行队列+异步执行

+(void)asyncSerial{

NSLog(@"asyncSerial---begin");

dispatch_queue_t queue = dispatch_queue_create("test.queue", DISPATCH_QUEUE_SERIAL);

    dispatch_async(queue, ^{

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

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

}

});

dispatch_async(queue, ^{

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

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

}

});

dispatch_async(queue, ^{


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

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

}

});

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

}

执行结果

2016-03-13 19:07:35.119 多线程_Demo[3812:271447] asyncSerial---begin


2016-03-13 19:07:35.119 多线程_Demo[3812:271447] asyncSerial---end


2016-03-13 19:07:35.119 多线程_Demo[3812:271569] 1------<NSThread: 0x608000261600>{number = 3, name = (null)}


2016-03-13 19:07:35.120 多线程_Demo[3812:271569] 1------<NSThread: 0x608000261600>{number = 3, name = (null)}


2016-03-13 19:07:35.120 多线程_Demo[3812:271569] 2------<NSThread: 0x608000261600>{number = 3, name = (null)}


2016-03-13 19:07:35.120 多线程_Demo[3812:271569] 2------<NSThread: 0x608000261600>{number = 3, name = (null)}


2016-03-13 19:07:35.120 多线程_Demo[3812:271569] 3------<NSThread: 0x608000261600>{number = 3, name = (null)}


2016-03-13 19:07:35.121 多线程_Demo[3812:271569] 3------<NSThread: 0x608000261600>{number = 3, name = (null)}

从执行结果可以看出,开启了一条新线程,但是任务还是串行,所以任务是一个一个执行。

主队列:

GCD自带的一种特殊的串行队列

所有放在主队列中的任务,都会放到主线程中执行

可使用dispatch_get_main_queue()获得主队列

主队列+同步执行

在主线程中调用,互等卡死。这是因为我们在主线程中执行任务,就把任务放到了主队列中,也就是主线程的队列中,而同步执行有一个特点,就是对于任务是立即执行。那么当我们把任务放到主队列之后,它就会立即执行。但是主线程现在正在执行当前方法,所以任务要等到当前方法执行完毕之后才能执行。而当前方法又要等到任务执行完成之后才能往下执行,就出现互相等待的卡死现象。

如果是在非主线程中调用,则不会出现卡死现象,在主线程中任务一个接一个执行

主队列+异步执行

只在主线程中执行任务,执行完一个任务,再执行下一个任务

GCD线程之间的通讯

在iOS开发过程中,我们一般在主线程里边进行UI刷新,例如:点击、滚动、拖拽等事件。我们通常把一些耗时的操作放在其他线程,比如说图片下载、文件上传等耗时操作。而当我们有时候在其他线程完成了耗时操作时,需要回到主线程,那么就用到了线程之间的通讯。

+(void)backToMainQueue{

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

sleep(3);

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

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

}

dispatch_async(dispatch_get_main_queue(), ^{

NSLog(@"刷新UI界面");

});

});

}

GCD的其他方法

GCD的延时执行方法 dispatch_after

当我们需要延迟执行一段代码时,就需要用到GCD的dispatch_after方法。

+(void)afterTime{

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{

// 2秒后异步执行这里的代码...

NSLog(@"run-----");

});

}

GCD的一次性代码(只执行一次) dispatch_once

我们在创建单例、或者有整个程序运行过程中只执行一次的代码时,我们就用到了GCD的dispatch_once方法。使用dispatch_once函数能保证某段代码在程序运行过程中只被执行1次。

+(void)operationOnce{

static dispatch_once_t onceToken;

dispatch_once(&onceToken, ^{

// 只执行1次的代码(这里面默认是线程安全的)

NSLog(@"once----");

});

}

GCD的快速迭代方法 dispatch_apply

通常我们会用for循环遍历,但是GCD给我们提供了快速迭代的方法dispatch_apply,使我们可以同时遍历。比如说遍历0~5这6个数字,for循环的做法是每次取出一个元素,逐个遍历。dispatch_apply可以同时遍历多个数字。

+(void)anyApply{

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

dispatch_apply(6, queue, ^(size_t index) {

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

});

}

执行结果

2016-03-13 20:21:32.175 多线程_Demo[4395:304488] 1------<NSThread: 0x618000068f40>{number = 1, name = main}

2016-03-13 20:21:32.175 多线程_Demo[4395:304577] 0------<NSThread: 0x60000006ac00>{number = 6, name = (null)}

2016-03-13 20:21:32.176 多线程_Demo[4395:305754] 2------<NSThread: 0x608000076a80>{number = 7, name = (null)}

2016-03-13 20:21:32.176 多线程_Demo[4395:305755] 4------<NSThread: 0x61000006a540>{number = 9, name = (null)}

2016-03-13 20:21:32.176 多线程_Demo[4395:305753] 3------<NSThread: 0x618000079540>{number = 8, name = (null)}

2016-03-13 20:21:32.176 多线程_Demo[4395:305756] 5------<NSThread: 0x60000006b180>{number = 10, name = (null)}

GCD的栅栏方法 dispatch_barrier_async

我们有时需要异步执行两组操作,而且第一组操作执行完之后,才能开始执行第二组操作。这样我们就需要一个相当于栅栏一样的一个方法将两组异步执行的操作组给分割起来,当然这里的操作组里可以包含一个或多个任务。这就需要用到dispatch_barrier_async方法在两个操作组间形成栅栏。

+(void)barrier{

dispatch_queue_t queue = dispatch_queue_create("12312312", DISPATCH_QUEUE_CONCURRENT);

dispatch_async(queue, ^{

sleep(2);

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

});

dispatch_async(queue, ^{

sleep(1);

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

});

dispatch_barrier_async(queue, ^{

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

});

dispatch_async(queue, ^{

sleep(2);

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

});

dispatch_async(queue, ^{

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

});

}

执行结果

2016-03-13 20:19:59.033 多线程_Demo[4395:304592] ----2-----<NSThread: 0x608000076bc0>{number = 3, name = (null)}

2016-03-13 20:20:00.034 多线程_Demo[4395:304574] ----1-----<NSThread: 0x610000066ac0>{number = 4, name = (null)}

2016-03-13 20:20:00.034 多线程_Demo[4395:304574] ----barrier-----<NSThread: 0x610000066ac0>{number = 4, name = (null)}

2016-03-13 20:20:00.034 多线程_Demo[4395:304575] ----4-----<NSThread: 0x600000068a00>{number = 5, name = (null)}

2016-03-13 20:20:02.035 多线程_Demo[4395:304574] ----3-----<NSThread: 0x610000066ac0>{number = 4, name = (null)}

GCD的队列组 dispatch_group

有时候我们会有这样的需求:分别异步执行2个耗时操作,然后当2个耗时操作都执行完毕后再回到主线程执行操作。这时候我们可以用到GCD的队列组。

  • 我们可以先把任务放到队列中,然后将队列放入队列组中。
  • 调用队列组的dispatch_group_notify回到主线程执行操作。

+(void)group{

dispatch_group_t group =  dispatch_group_create();

dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

// 执行1个耗时的异步操作

NSLog(@"正在执行第一个下载任务");

sleep(3);

NSLog(@"第一个下载任务执行完成");

});

dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

// 执行1个耗时的异步操作

NSLog(@"正在执行第二个下载任务");

sleep(1);

NSLog(@"第二个下载任务执行完成");

});

dispatch_group_notify(group, dispatch_get_main_queue(), ^{

// 等前面的异步操作都执行完毕后,回到主线程...

NSLog(@"下载任务执行完成");

});

}

执行结果

2016-03-13 20:18:07.112 多线程_Demo[4352:303197] 正在执行第二个下载任务

2016-03-13 20:18:07.112 多线程_Demo[4352:303211] 正在执行第一个下载任务

2016-03-13 20:18:08.115 多线程_Demo[4352:303197] 第二个下载任务执行完成

2016-03-13 20:18:10.115 多线程_Demo[4352:303211] 第一个下载任务执行完成

2016-03-13 20:18:10.115 多线程_Demo[4352:303154] 下载任务执行完成

多线程开发之GCD的更多相关文章

  1. iOS多线程开发之GCD(中篇)

    前文回顾: 上篇博客讲到GCD的实现是由队列和任务两部分组成,其中获取队列的方式有两种,第一种是通过GCD的API的dispatch_queue_create函数生成Dispatch Queue:第二 ...

  2. iOS多线程开发之GCD(死锁篇)

    上篇和中篇讲解了什么是GCD,如何使用GCD,这篇文章将讲解使用GCD中将遇到的死锁问题.有兴趣的朋友可以回顾<iOS多线程开发之GCD(上篇)>和<iOS多线程开发之GCD(中篇) ...

  3. iOS多线程开发之GCD(中级篇)

    前文回顾: 上篇博客讲到GCD的实现是由队列和任务两部分组成,其中获取队列的方式有两种,第一种是通过GCD的API的dispatch_queue_create函数生成Dispatch Queue:第二 ...

  4. iOS多线程开发之GCD(基础篇)

    总纲: GCD基本概念 GCD如何实现 GCD如何使用 队列和任务组合 一.GCD基本概念 GCD 全称Grand Central Dispatch(大中枢队列调度),是一套低层API,提供了⼀种新的 ...

  5. iOS多线程开发之NSOperation - 快上车,没时间解释了!

    一.什么是NSOperation? NSOperation是苹果提供的一套多线程解决方案.实际上NSOperation是基于GCD更高一层的封装,但是比GCD更加的面向对象.代码可读性更高.可控性更强 ...

  6. iOS多线程开发之NSOperation

    一.什么是NSOperation? NSOperation是苹果提供的一套多线程解决方案.实际上NSOperation是基于GCD更高一层的封装,但是比GCD更加的面向对象.代码可读性更高.可控性更强 ...

  7. iOS 开发之 GCD 不同场景使用

    header{font-size:1em;padding-top:1.5em;padding-bottom:1.5em} .markdown-body{overflow:hidden} .markdo ...

  8. iOS 开发之 GCD 基础

    header{font-size:1em;padding-top:1.5em;padding-bottom:1.5em} .markdown-body{overflow:hidden} .markdo ...

  9. Java 多线程开发之 Callable 与线程池

    前言 我们常见的创建线程的方式有 2 种:继承 Thread 和 实现 Runnable 接口. 其实,在 JDK 中还提供了另外 2 种 API 让开发者使用. 二.简单介绍 2.1 Callabl ...

随机推荐

  1. leetcode814 Binary Tree Pruning

    """ We are given the head node root of a binary tree, where additionally every node's ...

  2. 白手起家Django项目发布上篇_linux centos 环境部署

    在项目发布之前,首先准备好我们编写好的Django项目,这个我们在之后博客有写,大家可以去看, 首先,先开始安装linux服务器,作为Django项目的发布服务器.以Vmware虚拟机为例子,大家也可 ...

  3. MySQL查询事务 杀死事务

    遇到 com.mysql.jdbc.exceptions.jdbc4.MySQLTransactionRollbackException: Deadlock 查询: SELECT * FROM inf ...

  4. Console-terminal-tty-shell-kernel

    Console-terminal-tty-shell-kernel 1. 先看图表     1.1 简表     1.2 shell与内核的示意图     1.3 Console-terminal-t ...

  5. java#内部类和嵌套类

    内容思路来自Java编程思想,个人读书做的笔记,仅个人复习之用,故他人参考请自行辨别内容是否有错误. 在类的类部可以定义类,叫做内部类.如果这个内部类被static修饰,此时内部的类叫做嵌套类. 内部 ...

  6. java List 去重方式及效率对比

    01 实现思路:使用两个for循环遍历集合所有元素,然后进行判断是否有相同元素,如果有,则去除.这种方式是大部分最先想到的,也是最简单的实现方式.其中,这种方式可以保证List集合原来的顺序不变. 代 ...

  7. [题解] LuoguP2257 YY的GCD

    传送门 给\(n,m\),让你求 \[ \sum\limits_{i=1}^n \sum\limits_{j=1}^m [\gcd(i,j) \in prime] \] 有\(T\)组询问\((T \ ...

  8. Java If ... Else

    章节 Java 基础 Java 简介 Java 环境搭建 Java 基本语法 Java 注释 Java 变量 Java 数据类型 Java 字符串 Java 类型转换 Java 运算符 Java 字符 ...

  9. Mac安装软件提示破损

    安装提示破损 zhong终端输入 sudo spctl --master-disable 就可以顺利打开啦

  10. HDU - 6000 Wash(优先队列+贪心)

    题意:已知有L件衣服,M个洗衣机,N个烘干机,已知每个机器的工作时间,且每个机器只能同时处理一件衣服,问洗烘完所有衣服所需的最短时间. 分析: 1.优先队列处理出每件衣服最早的洗完时间. 2.优先队列 ...