GCD是iOS的一种底层多线程机制,今天总结一下GCD的常用API和概念,希望对大家的学习起到帮助作用。

GCD队列的概念

在多线程开发当中,程序员只要将想做的事情定义好,并追加到DispatchQueue(派发队列)当中就好了。

派发队列分为两种,一种是串行队列(SerialDispatchQueue),一种是并行队列(ConcurrentDispatchQueue)。

一个任务就是一个block,比如,将任务添加到队列中的代码是:

 dispatch_async(queue, block);

当给queue添加多个任务时,如果queue是串行队列,则它们按顺序一个个执行,同时处理的任务只有一个。

当queue是并行队列时,不论第一个任务是否结束,都会立刻开始执行后面的任务,也就是可以同时执行多个任务。

但是并行执行的任务数量取决于XNU内核,是不可控的。比如,如果同时执行10个任务,那么10个任务并不是开启10个线程,线程会根据任务执行情况复用,由系统控制。

获取队列

系统提供了两个队列,一个是MainDispatchQueue,一个是GlobalDispatchQueue。

前者会将任务插入主线程的RunLoop当中去执行,所以显然是个串行队列,我们可以使用它来更新UI。

后者则是一个全局的并行队列,有高、默认、低和后台4个优先级。

它们的获取方式如下:

 dispatch_queue_t queue = dispatch_get_main_queue();

 dispatch queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRORITY_DEFAULT, )

执行异步任务

     dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, );
dispatch_async(queue, ^{
//...
});

这个代码片段直接在子线程里执行了一个任务块。使用GCD方式任务是立即开始执行的

它不像操作队列那样可以手动启动,同样,缺点也是它的不可控性。

令任务只执行一次

 + (id)shareInstance {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_shareInstance = [[self alloc] init];
});
}

这种只执行一次且线程安全的方式经常出现在单例构造器当中。

任务组

有时候,我们希望多个任务同时(在多个线程里)执行,再他们都完成之后,再执行其他的任务,

于是可以建立一个分组,让多个任务形成一个组,下面的代码在组中多个任务都执行完毕之后再执行后续的任务:

     dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, );
dispatch_group_t group = dispatch_group_create(); dispatch_group_async(group, queue, ^{ NSLog(@""); });
dispatch_group_async(group, queue, ^{ NSLog(@""); });
dispatch_group_async(group, queue, ^{ NSLog(@""); });
dispatch_group_async(group, queue, ^{ NSLog(@""); });
dispatch_group_async(group, queue, ^{ NSLog(@""); }); dispatch_group_notify(group, dispatch_get_main_queue(), ^{ NSLog(@"done"); });

延迟执行任务

1     dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
2 //...
3 });

这段代码将会在10秒后将任务插入RunLoop当中。

dispatch_asycn和dispatch_sync

先前已经有过一个使用dispatch_async执行异步任务的一个例子,下面来看一段代码:

     dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, );

     dispatch_async(queue, ^{
NSLog(@"");
}); NSLog(@"");

这段代码首先获取了全局队列,也就是说,dispatch_async当中的任务被丢到了另一个线程里去执行,async在这里的含义是,当当前线程给子线程分配了block当中的任务之后,当前线程会立即执行,并不会发生阻塞,也就是异步的。那么,输出结果不是12就是21,因为我们没法把控两个线程RunLoop里到底是怎么执行的。

类似的,还有一个“同步”方法dispatch_sync,代码如下:

     dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, );

     dispatch_sync(queue, ^{
NSLog(@"");
}); NSLog(@"");

这就意味着,当主线程将任务分给子线程后,主线程会等待子线程执行完毕,再继续执行自身的内容,那么结果显然就是12了。

需要注意的一点是,这里用的是全局队列,那如果把dispatch_sync的队列换成主线程队列会怎么样呢:

     dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_sync(queue, ^{
NSLog(@"");
});

这段代码会发生死锁,因为:

1.主线程通过dispatch_sync把block交给主队列后,会等待block里的任务结束再往下走自身的任务,

2.而队列是先进先出的,block里的任务也在等待主队列当中排在它之前的任务都执行完了再走自己。

这种循环等待就形成了死锁。所以在主线程当中使用dispatch_sync将任务加到主队列是不可取的。

创建队列

我们可以使用系统提供的函数获取主串行队列和全局并行队列,当然也可以自己手动创建串行和并行队列,代码为:

1     dispatch_queue_t mySerialDispatchQueue = dispatch_queue_create("com.Steak.GCD", DISPATCH_QUEUE_SERIAL);
2 dispatch_queue_t myConcurrentDispatchQueue = dispatch_queue_create("com.Steak.GCD", DISPATCH_QUEUE_CONCURRENT);

在MRC下,手动创建的队列是需要释放的

1     dispatch_release(myConcurrentDispatchQueue);

手动创建的队列和默认优先级全局队列优先级等同,如果需要修改队列的优先级,需要:

1     dispatch_queue_t myConcurrentDispatchQueue = dispatch_queue_create("com.Steak.GCD", DISPATCH_QUEUE_CONCURRENT);
2 dispatch_queue_t targetQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
3 dispatch_set_target_queue(myConcurrentDispatchQueue, targetQueue);

上面的代码修改队列的优先级为后台级别,即与默认的后台优先级的全局队列等同。

串行、并行队列与读写安全性

在向串行队列(SerialDispatchQueue)当中加入多个block任务后,一次只能同时执行一个block,如果生成了n个串行队列,并且向每个队列当中都添加了任务,那么系统就会启动n个线程来同时执行这些任务。

对于串行队列,正确的使用时机,是在需要解决数据/文件竞争问题时使用它。比如,我们可以令多个任务同时访问一块数据,这样会出现冲突,也可以把每个操作都加入到一个串行队列当中,因为串行队列一次只能执行一个线程的任务,所以不会出现冲突。

但是考虑到串行队列会因为上下文切换而拖慢系统性能,所以我们还是很期望采用并行队列的,来看下面的示例代码:

 1     dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
2 dispatch_async(queue, ^{
3 //数据读取
4 });
5 dispatch_async(queue, ^{
6 //数据读取2
7 });
8 dispatch_async(queue, ^{
9 //数据写入
10 });
11 dispatch_async(queue, ^{
12 //数据读取3
13 });
14 dispatch_async(queue, ^{
15 //数据读取4
16 });

显然,这5个操作的执行顺序是我们无法预期的,我们希望在读取1和读取2执行结束后,再执行写入,写入完成后再执行读取3和读取4。

为了实现这个效果,这里可以使用GCD的另一个API:

1     dispatch_barrier_async(queue, ^{
2 //数据写入
3 });

这样就保证的写入操作的并发安全性。

对于没有数据竞争的并行操作,则可以使用并行队列(CONCURRENT)来实现。

JOIN行为

CGD利用dispatch_group_wait来实现多个操作的join行为,代码如下:

 1     dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
2 dispatch_group_t group = dispatch_group_create();
3
4 dispatch_group_async(group, queue, ^{
5 sleep(0.5);
6 NSLog(@"1");
7 });
8 dispatch_group_async(group, queue, ^{
9 sleep(1.5);
10 NSLog(@"2");
11 });
12 dispatch_group_async(group, queue, ^{
13 sleep(2.5);
14 NSLog(@"3");
15 });
16
17 NSLog(@"aaaaa");
18
19 dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 2ull * NSEC_PER_SEC);
20 if (dispatch_group_wait(group, time) == 0) {
21 NSLog(@"已经全部执行完毕");
22 }
23 else {
24 NSLog(@"没有执行完毕");
25 }
26
27 NSLog(@"bbbbb");

这里起了3个异步线程放在一个组里,之后通过dispatch_time_t创建了一个超时时间(2秒),程序之后行,立即输出了aaaaa,这是主线程输出的,当遇到dispatch_group_wait时,主线程会被挂起,等待2秒,在等待的过程当中,子线程分别输出了1和2,2秒时间达到后,主线程发现组里的任务并没有全部结束,然后输出了bbbbb。

在这里,如果超时时间设置得比较长(比如5秒),那么会在2.5秒时第三个任务结束后,立即输出bbbbb,也就是说,当组中的任务全部执行完毕时,主线程就不再被阻塞了。

如果希望永久等待下去,时间可以设置为DISPATCH_TIME_FOREVER。

并行循环

类似于C#的PLINQ,OC也可以让循环并行执行,在GCD当中有一个dispatch_apply函数:

1     dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
2 dispatch_apply(20, queue, ^(size_t i) {
3 NSLog(@"%lu", i);
4 });

这段代码让i并行循环了20次,如果内部处理的是一个数组,就可以实现对数组的并行循环了,它的内部是dispatch_sync的同步操作,所以在执行这个循环的过程当中,当前线程会被阻塞。

暂停和恢复

使用dispatch_suspend(queue)可以暂停队列中任务的执行,使用dispatch_result(queue)可以继续执行被暂停的队列。

iOS开发之GCD使用总结的更多相关文章

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

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

  2. iOS 开发之 GCD 基础

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

  3. iOS开发之GCD基础

    重新回顾.学习GCD.Block.先贴出一篇不错的讲解GCD基础使用的文章 原文地址:http://blog.csdn.net/aolan1108/article/details/17283415 做 ...

  4. iOS开发之GCD

    GCD,全称Grand Central Dispath,是苹果开发的一种支持并行操作的机制.它的主要部件是一个FIFO队列和一个线程池,前者用来添加任务,后者用来执行任务. GCD中的FIFO队列称为 ...

  5. iOS开发之GCD总结

    直接贴出常用的函数,方便要用的时候直接使用. -------------     type 1 ---------------- 说明  : 创建一个dispatch_group_t,每次网络请求前先 ...

  6. iOS开发之GCD同步主线程、异步主线程

    /** 在主线程执行block */ + (void)gs_synExecuteOnMainThread:(void (^)(void))block { if ((nil == block) || ( ...

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

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

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

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

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

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

随机推荐

  1. Myeclipse 2014配置SVN详细图解

    1.什么是SVN? 管理软件开发过程中的版本控制工具. 2.myeclipse安装SVN插件步骤,以myeclipse 2014为例. (1)下载SVN插件 http://subclipse.tigr ...

  2. Linux 上的数据可视化工具

    Linux 上的数据可视化工具 5 种开放源码图形化工具简介 Linux® 上用来实现数据的图形可视化的应用程序有很多,从简单的 2-D 绘图到 3-D 制图,再到科学图形编程和图形模拟.幸运的是,这 ...

  3. ASP.NET-Web-API-Poster.pdf flow chart

    下载地址

  4. 轻松配置java开发环境

    1.下载java开发常用的IDE(Integrated Development Environment)--eclipse.http://www.eclipse.org/downloads/ 2.下载 ...

  5. 【POJ 3009 Curling2.0 迷宫寻径 DFS】

    http://poj.org/problem?id=3009 模拟冰壶的移动,给出到达终点的最少投掷次数(不可达时为-1). 具体移动规则如下: 每次选四个方向之一,沿此方向一直前进,直到撞到bloc ...

  6. HDOJ-1003 Max Sum(最大连续子段 动态规划)

    http://acm.hdu.edu.cn/showproblem.php?pid=1003 给出一个包含n个数字的序列{a1,a2,..,ai,..,an},-1000<=ai<=100 ...

  7. 关于CoreData的理解和使用.

    CoreData是苹果官方推出的一种方便的面向对象的存储方式,相信大家都已经对其有所了解,但是对于CoreData的概念大家都存在部分的误区.给大家推荐个网址是苹果的官方文档的翻译版(http://o ...

  8. C#委托的异步调用【转】

    本文将主要通过“同步调用”.“异步调用”.“异步回调”三个示例来讲解在用委托执行同一个“加法类”的时候的的区别和利弊. 首先,通过代码定义一个委托和下面三个示例将要调用的方法: ); //模拟该方法运 ...

  9. 呵呵!手把手带你在 IIS 上执行 Python

    公司的站点让我头痛死了.在众多前辈高手的带领下.一大堆的 CMD 在站点里执行得好好地,黑客攻击也好好地.仅仅有站点和我不好好地,我快累死了,站点快挂了.. . 为了解决问题.我想到了 Python ...

  10. 从linux telnet到exchange邮件server来測试发送邮件

    我们在Linux下,能够通过telnet邮件server的25port(一般smtp邮件server都是这个),来測试是否能发送邮件. 前提是先得把DNS配好,或者/etc/hosts文件制定好邮件s ...