我会从多少个角度浅谈自身对GCD的驾驭,一般都会谈到三种艺术

谈到iOS三十六线程,一般都会谈到七种方法:pthread、NSThread、GCD和NSOperation。其中,苹果推荐也是大家最日常利用的无疑是GCD。对于身为开发者的大家来说,并发平素都很讨厌,如若对GCD的了然不够透彻,那么iOS开发的历程相对不会顺手。那里,我会从多少个角度浅谈自己对GCD的明亮。

谈到iOS二十四线程,一般都会谈到种种办法:pthread、NSThread、GCD和NSOperation。其中,苹果推荐也是我们最平日采纳的的确是GCD。对于身为开发者的大家的话,并发一向都很伤脑筋,若是对GCD的领会不够透彻,那么iOS开发的进度绝对不会顺畅。那里,我会从几个角度浅谈自身对GCD的接头。

一、十二线程背景

Although threads have been around for many years and continue to have
their uses, they do not solve the general problem of executing
multiple tasks in a scalable way. With threads, the burden of creating
a scalable solution rests squarely on the shoulders of you, the
developer. You have to decide how many threads to create and adjust
that number dynamically as system conditions change. Another problem
is that your application assumes most of the costs associated with
creating and maintaining any threads it uses.

上述差不多说出了平昔操纵线程已毕十二线程的弊病:

  • 开发人士必须依照系统的成形动态调整线程的数据和处境,即对开发者的负担重。
  • 应用程序会在开创和保安线程上消耗很多财力,即效能低。

相对的,GCD是一套低层级的C API,通过
GCD,开发者只须求向队列中添加一段代码块(block或C函数指针),而不须要平素和线程打交道。GCD在后端管理着一个线程池,它不光控制着你的代码块将在哪些线程被执行,还按照可用的系统资源对那个线程实行管制。GCD的行事措施,使其持有不少优点(快、稳、准):

  • 快,更快的内存作用,因为线程栈不暂存于应用内存。
  • 稳,提供了机动的和周详的线程池管理机制,稳定而方便。
  • 准,提供了直接并且简单的调用接口,使用方便,准确。

一、三十二线程背景

Although threads have been around for many years and continue to have
their uses, they do not solve the general problem of executing
multiple tasks in a scalable way. With threads, the burden of creating
a scalable solution rests squarely on the shoulders of you, the
developer. You have to decide how many threads to create and adjust
that number dynamically as system conditions change. Another problem
is that your application assumes most of the costs associated with
creating and maintaining any threads it uses.

上述几乎说出了直接操纵线程落成八线程的弊端:

  • 开发人员必须根据系统的更动动态调整线程的数量和情况,即对开发者的负担重。
  • 应用程序会在开立和维护线程上消耗过多本钱,即成效低。

周旋的,GCD是一套低层级的C API,通过
GCD,开发者只需求向队列中添加一段代码块(block或C函数指针),而不必要直接和线程打交道。GCD在后端管理着一个线程池,它不但决定着您的代码块将在哪个线程被实施,还根据可用的系统资源对这一个线程举办管理。GCD的干活措施,使其拥有众多优点(快、稳、准):

  • 快,更快的内存功能,因为线程栈不暂存于应用内存。
  • 稳,提供了机关的和宏观的线程池管理机制,稳定而方便。
  • 准,提供了一贯并且不难的调用接口,使用方便,准确。

二、队列和任务

初学GCD的时候,肯定会纠结一些近乎很重大但却毫无意义的难点。比如:GCD和线程到底什么关系;异步义务到底在哪些线程工作;队列到底是个怎么着事物;mian
queue和main
thread到底搞什么名堂等等。现在,那些我们直接略过(最终拾遗中会谈一下),苹果既然推荐应用GCD,那么为什么还要纠结于线程呢?须要关注的唯有七个概念:队列、任务。

二、队列和职务

初学GCD的时候,肯定会纠结一些好像很关键但却毫无意义的标题。比如:GCD和线程到底哪些关系;异步职责到底在哪个线程工作;队列到底是个什么东西;mian
queue和main
thread到底搞哪样名堂等等。现在,这几个大家直接略过(最终拾遗中会谈一下),苹果既然推荐使用GCD,那么为啥还要纠结于线程呢?须求关怀的唯有四个概念:队列、职责。

1. 队列

调度队列是一个对象,它会以first-in、first-out的法门管理您提交的天职。GCD有二种队列类型:

  • 串行队列,串行队列将义务以先进先出(FIFO)的逐条来举行,所以串行队列平常用来做访问一些特定资源的协同处理。你能够也按照须要创立几个种类,而那么些队列相对其余队列都是出新执行的。换句话说,即便你成立了4个串行队列,每一个行列在同一时间都只进行一个义务,对那八个义务的话,他们是互相独立且并发执行的。即使急需创设串行队列,一般用dispatch_queue_create那一个点子来落实,并点名队列类型DISPATCH_QUEUE_SERIAL。
  • 相互队列,并发队列纵然是能而且施行三个职责,但那几个任务仍然是安分守己先到先举行(FIFO)的逐一来推行的。并发队列会基于系统负荷来方便地采用并发执行那几个义务。并发队列一般指的就是大局队列(Global
    queue),进度中存在多少个全局队列:高、中(默许)、低、后台多个优先级队列,可以调用dispatch_get_global_queue函数传入优先级来访问队列。当然大家也得以用dispatch_queue_create,并点名队列类型DISPATCH_QUEUE_CONCURRENT,来协调创办一个产出队列。
  • 主队列,与主线程成效相同。实际上,提交至main
    queue的职务会在主线程中推行。main
    queue可以调用dispatch_get_main_queue()来收获。因为main
    queue是与主线程相关的,所以这是一个串行队列。和其余串行队列一样,那么些队列中的职责一回只可以举行一个。它能担保拥有的职责都在主线程执行,而主线程是唯一可用来更新
    UI 的线程。

外加说一句,上边也说过,队列间的执行是互相的,不过也设有一些范围。比如,并行执行的行列数量受到内核数的限定,无法真正形成大量队列并行执行;比如,对于相互队列中的全局队列而言,其存在优先级关系,执行的时候也会安分守己其优先顺序,而不是互相。

1. 队列

调度队列是一个对象,它会以first-in、first-out的办法管理您提交的任务。GCD有三种队列类型:

  • 串行队列,串行队列将职分以先进先出(FIFO)的次第来实施,所以串行队列平时用来做访问一些特定资源的联手处理。你可以也依照必要创制三个种类,而那一个队列相对其余队列都是出新执行的。换句话说,假使您创设了4个串行队列,每一个队列在同一时间都只举办一个职务,对那八个任务的话,他们是互相独立且并发执行的。若是急需创立串行队列,一般用dispatch_queue_create那几个措施来兑现,并指定队列类型DISPATCH_QUEUE_SERIAL。

  • 互动队列,并发队列即便是能同时实施多少个职务,但那几个职务仍然是根据先到先举行(FIFO)的一一来实施的。并发队列会基于系统负荷来适合地挑选并发执行这么些任务。并发队列一般指的就是大局队列(Global
    queue),进程中留存七个全局队列:高、中(默许)、低、后台八个优先级队列,可以调用dispatch_get_global_queue函数传入优先级来拜会队列。当然大家也得以用dispatch_queue_create,并点名队列类型DISPATCH_QUEUE_CONCURRENT,来协调创办一个油但是生队列。

  • 主队列,与主线程作用相同。实际上,提交至main
    queue的职分会在主线程中执行。main
    queue可以调用dispatch_get_main_queue()来取得。因为main
    queue是与主线程相关的,所以那是一个串行队列。和其他串行队列一样,那个行列中的职务一遍只能够执行一个。它能确保所有的职责都在主线程执行,而主线程是绝无仅有可用于立异UI 的线程。

额外说一句,上边也说过,队列间的施行是互相的,可是也存在有的限量。比如,并行执行的体系数量受到内核数的范围,不能真正做到多量行列并行执行;比如,对于互相队列中的全局队列而言,其设有优先级关系,执行的时候也会服从其事先顺序,而不是相互。

2. 任务

linux内核中的任务的概念是描述进度的一种结构体,而GCD中的义务只是一个代码块,它可以指一个block或者函数指针。依照那么些代码块添加进去队列的不二法门,将义务分为同步任务和异步职务:

  • 联合义务,使用dispatch_sync将职责加入队列。将一起职务出席串行队列,会相继执行,一般不这么做而且在一个任务未终止时调起其它同步职分会死锁。将联合义务参预并行队列,会挨个执行,可是也没怎么意思。
  • 异步职分,使用dispatch_async将职分插手队列。将异步任务参加串行队列,会相继执行,并且不会现出死锁难点。将异步职分插手并行队列,会并行执行多少个职分,那也是大家最常用的一种方式。

2. 任务

linux内核中的义务的概念是讲述进度的一种结构体,而GCD中的职务只是一个代码块,它可以指一个block或者函数指针。根据这些代码块添加进去队列的法门,将职分分为同步职务和异步义务:

  • 一路职务,使用dispatch_sync将义务参预队列。将联合义务插手串行队列,会相继执行,一般不这么做而且在一个任务未竣事时调起其他同步义务会死锁。将一块职责参预并行队列,会挨个执行,可是也没怎么含义。
  • 异步职分,使用dispatch_async将任务插手队列。将异步职责插手串行队列,会相继执行,并且不会现出死锁难点。将异步职责参加并行队列,会并行执行三个任务,那也是大家最常用的一种格局。

3. 粗略利用

// 队列的创建,queue1:中(默认)优先级的全局并行队列、queue2:主队列、queue3:未指定type则为串行队列、queue4:指定串行队列、queue5:指定并行队列
dispatch_queue_t queue1 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_queue_t queue2 = dispatch_get_main_queue();
dispatch_queue_t queue3 = dispatch_queue_create("queue3", NULL);
dispatch_queue_t queue4 = dispatch_queue_create("queue4", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t queue5 = dispatch_queue_create("queue5", DISPATCH_QUEUE_CONCURRENT);

// 队列中添加异步任务
dispatch_async(queue1, ^{
// 任务
...
});

// 队列中添加同步任务
dispatch_sync(queue1, ^{
// 任务
...
});

3. 概括利用

// 队列的创建,queue1:中(默认)优先级的全局并行队列、queue2:主队列、queue3:未指定type则为串行队列、queue4:指定串行队列、queue5:指定并行队列
dispatch_queue_t queue1 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_queue_t queue2 = dispatch_get_main_queue();
dispatch_queue_t queue3 = dispatch_queue_create("queue3", NULL);
dispatch_queue_t queue4 = dispatch_queue_create("queue4", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t queue5 = dispatch_queue_create("queue5", DISPATCH_QUEUE_CONCURRENT);

// 队列中添加异步任务
dispatch_async(queue1, ^{
    // 任务
    ...
});

// 队列中添加同步任务
dispatch_sync(queue1, ^{
    // 任务
    ...
});

三、GCD常见用法和使用场景

卓殊喜欢一句话:Talk is cheap, show me the
code.接下来对GCD的采纳,我会通过代码浮现。

三、GCD常见用法和动用场景

老大喜欢一句话:Talk is cheap, show me the
code.接下来对GCD的应用,我会通过代码呈现。

1. dispatch_async

诚如用法

dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(globalQueue, ^{
    // 一个异步的任务,例如网络请求,耗时的文件操作等等
    ...
    dispatch_async(dispatch_get_main_queue(), ^{
        // UI刷新
        ...
    });
});

运用场景
那种用法非平常见,比如敞开一个异步的互连网请求,待数额重返后回来主队列刷新UI;又例如请求图片,待图片再次来到刷新UI等等。

1. dispatch_async

诚如用法

dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(globalQueue, ^{
    // 一个异步的任务,例如网络请求,耗时的文件操作等等
    ...
    dispatch_async(dispatch_get_main_queue(), ^{
        // UI刷新
        ...
    });
});

行使场景
那种用法万分广泛,比如敞开一个异步的互联网请求,待数额重临后回到主队列刷新UI;又例如请求图片,待图片再次回到刷新UI等等。

2. dispatch_after

相似用法

dispatch_queue_t queue= dispatch_get_main_queue();
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0 * NSEC_PER_SEC)), queue, ^{
    // 在queue里面延迟执行的一段代码
    ...
});

行使场景
那为大家提供了一个简单的推迟执行的措施,比如在view加载为止延迟执行一个卡通等等。

2. dispatch_after

诚如用法

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0 * NSEC_PER_SEC)), queue, ^{
    // 在queue里面延迟执行的一段代码
    ...
});

运用场景
这为大家提供了一个大致的推移执行的主意,比如在view加载截至延迟执行一个动画片等等。

3. dispatch_once

一般用法

static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
    // 只执行一次的任务
    ...
});

选取场景
可以动用其成立一个单例,也可以做一些任何只进行三次的代码,比如做一个只好点一遍的button(好像没啥用)。

3. dispatch_once

诚如用法

static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
    // 只执行一次的任务
    ...
});

运用场景
可以选择其创立一个单例,也足以做一些其余只进行四遍的代码,比如做一个只好点五遍的button(好像没啥用)。

4. dispatch_group

诚如用法

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();

dispatch_group_async(group, queue, ^{
    // 异步任务1
});

dispatch_group_async(group, queue, ^{
    // 异步任务2
});

// 等待group中多个异步任务执行完毕,做一些事情,介绍两种方式

// 方式1(不好,会卡住当前线程)
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
...

// 方式2(比较好)
dispatch_group_notify(group, mainQueue, ^{
    // 任务完成后,在主队列中做一些操作
    ...
});

运用场景
上述的一种方法,可以适用于自己维护的有些异步职务的联名难点;然则对于曾经封装好的有的库,比如AFNetworking等,我们不到手其异步义务的体系,那里可以通过一种计数的方法控制职分间协同,下边为杀鸡取卵单界面多接口的一种艺术。

// 两个请求和参数为我项目里面的不用在意。

// 计数+1
dispatch_group_enter(group);
[JDApiService getActivityDetailWithActivityId:self.activityId Location:stockAddressId SuccessBlock:^(NSDictionary *userInfo) {
    // 数据返回后一些处理
    ...

    // 计数-1
    dispatch_group_leave(group);
} FailureBlock:^(NSError *error) {
    // 数据返回后一些处理
    ...

    // 计数-1
    dispatch_group_leave(group);
}];

// 计数+1
dispatch_group_enter(group);
[JDApiService getAllCommentWithActivityId:self.activityId PageSize:3 PageNum:self.commentCurrentPage SuccessBlock:^(NSDictionary *userInfo) {
    // 数据返回后一些处理
    ...

    // 计数-1
    dispatch_group_leave(group);
} FailureBlock:^(NSError *error) {
    // 数据返回后一些处理
    ...

    // 计数-1
    dispatch_group_leave(group);
}];

// 其实用计数的说法可能不太对,但是就这么理解吧。会在计数为0的时候执行dispatch_group_notify的任务。
dispatch_group_notify(group, mainQueue, ^{
    // 一般为回主队列刷新UI
    ...
});

4. dispatch_group

相似用法

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();

dispatch_group_async(group, queue, ^{
    // 异步任务1
});

dispatch_group_async(group, queue, ^{
    // 异步任务2
});

// 等待group中多个异步任务执行完毕,做一些事情,介绍两种方式

// 方式1(不好,会卡住当前线程)
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
...

// 方式2(比较好)
dispatch_group_notify(group, mainQueue, ^{
    // 任务完成后,在主队列中做一些操作
    ...
});

应用场景
上述的一种艺术,可以适用于自己维护的一对异步职务的同台难题;不过对于早已封装好的部分库,比如AFNetworking等,大家不得到其异步任务的队列,那里可以经过一种计数的办法决定义务间一块,上面为缓解单界面多接口的一种格局。

// 两个请求和参数为我项目里面的不用在意。

// 计数+1
dispatch_group_enter(group);
[JDApiService getActivityDetailWithActivityId:self.activityId Location:stockAddressId SuccessBlock:^(NSDictionary *userInfo) {
    // 数据返回后一些处理
    ...

    // 计数-1
    dispatch_group_leave(group);
} FailureBlock:^(NSError *error) {
    // 数据返回后一些处理
    ...

    // 计数-1
    dispatch_group_leave(group);
}];

// 计数+1
dispatch_group_enter(group);
[JDApiService getAllCommentWithActivityId:self.activityId PageSize:3 PageNum:self.commentCurrentPage SuccessBlock:^(NSDictionary *userInfo) {
    // 数据返回后一些处理
    ...

    // 计数-1
    dispatch_group_leave(group);
} FailureBlock:^(NSError *error) {
    // 数据返回后一些处理
    ...

    // 计数-1
    dispatch_group_leave(group);
}];

// 其实用计数的说法可能不太对,但是就这么理解吧。会在计数为0的时候执行dispatch_group_notify的任务。
dispatch_group_notify(group, mainQueue, ^{
    // 一般为回主队列刷新UI
    ...
});

5. dispatch_barrier_async

一般用法

// dispatch_barrier_async的作用可以用一个词概括--承上启下,它保证此前的任务都先于自己执行,此后的任务也迟于自己执行。本例中,任务4会在任务1、2、3都执行完之后执行,而任务5、6会等待任务4执行完后执行。

dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
    // 任务1
    ...
});
dispatch_async(queue, ^{
    // 任务2
    ...
});
dispatch_async(queue, ^{
    // 任务3
    ...
});
dispatch_barrier_async(queue, ^{
    // 任务4
    ...
});
dispatch_async(queue, ^{
    // 任务5
    ...
});
dispatch_async(queue, ^{
    // 任务6
    ...
});

选择场景
和dispatch_group类似,dispatch_barrier也是异步任务间的一种共同方式,可以在诸如文件的读写操作时采用,有限协助读操作的准确性。其它,有少数要求注意,dispatch_barrier_sync和dispatch_barrier_async只在友好创制的并发队列上有效,在全局(Global)并发队列、串行队列上,效果跟dispatch_(a)sync效果一样。

5. dispatch_barrier_async

相似用法

// dispatch_barrier_async的作用可以用一个词概括--承上启下,它保证此前的任务都先于自己执行,此后的任务也迟于自己执行。本例中,任务4会在任务1、2、3都执行完之后执行,而任务5、6会等待任务4执行完后执行。

dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
    // 任务1
    ...
});
dispatch_async(queue, ^{
    // 任务2
    ...
});
dispatch_async(queue, ^{
    // 任务3
    ...
});
dispatch_barrier_async(queue, ^{
    // 任务4
    ...
});
dispatch_async(queue, ^{
    // 任务5
    ...
});
dispatch_async(queue, ^{
    // 任务6
    ...
});

行使场景
和dispatch_group类似,dispatch_barrier也是异步职分间的一种共同方式,可以在诸如文件的读写操作时行使,有限支撑读操作的准确性。此外,有几许亟需小心,dispatch_barrier_sync和dispatch_barrier_async只在大团结创造的并发队列上有效,在全局(Global)并发队列、串行队列上,效果跟dispatch_(a)sync效果同样。

6. dispatch_apply

一般用法

// for循环做一些事情,输出0123456789
for (int i = 0; i < 10; i ++) {
    NSLog(@"%d", i);
}

// dispatch_apply替换(当且仅当处理顺序对处理结果无影响环境),输出顺序不定,比如1098673452
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
/*! dispatch_apply函数说明
*
*  @brief  dispatch_apply函数是dispatch_sync函数和Dispatch Group的关联API
*         该函数按指定的次数将指定的Block追加到指定的Dispatch Queue中,并等到全部的处理执行结束
*
*  @param 10    指定重复次数  指定10次
*  @param queue 追加对象的Dispatch Queue
*  @param index 带有参数的Block, index的作用是为了按执行的顺序区分各个Block
*
*/
dispatch_apply(10, queue, ^(size_t index) {
    NSLog(@"%zu", index);
});

采纳场景
那么,dispatch_apply有如何用呢,因为dispatch_apply并行的运行机制,功用一般快于for循环的类串行机制(在for一次巡回中的处理职务过多时距离相比大)。比如那能够用来拉取互连网数据后提前算出各种控件的大小,避免绘制时计算,进步表单滑动流畅性,如果用for循环,耗时较多,并且每个表单的数码没有着重关系,所以用dispatch_apply比较好。

6. dispatch_apply

诚如用法

// for循环做一些事情,输出0123456789
for (int i = 0; i < 10; i ++) {
    NSLog(@"%d", i);
}

// dispatch_apply替换(当且仅当处理顺序对处理结果无影响环境),输出顺序不定,比如1098673452
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
/*! dispatch_apply函数说明
 *
 *  @brief  dispatch_apply函数是dispatch_sync函数和Dispatch Group的关联API
 *         该函数按指定的次数将指定的Block追加到指定的Dispatch Queue中,并等到全部的处理执行结束
 *
 *  @param 10    指定重复次数  指定10次
 *  @param queue 追加对象的Dispatch Queue
 *  @param index 带有参数的Block, index的作用是为了按执行的顺序区分各个Block
 *
 */
dispatch_apply(10, queue, ^(size_t index) {
    NSLog(@"%zu", index);
});

运用场景
那么,dispatch_apply有怎样用吧,因为dispatch_apply并行的运行机制,效能一般快于for循环的类串行机制(在for两遍巡回中的处理职分过多时差距相比较大)。比如那足以用来拉取网络数据后提前算出各类控件的轻重缓急,防止绘制时总括,升高表单滑动流畅性,如果用for循环,耗时较多,并且每个表单的多寡没有信赖关系,所以用dispatch_apply比较好。

7. dispatch_suspend和dispatch_resume

诚如用法

dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_suspend(queue); //暂停队列queue
dispatch_resume(queue);  //恢复队列queue

行使场景
那种用法我还未曾尝试过,不过里面有个必要小心的点。那七个函数不会潜移默化到行列中早就执行的任务,队列暂停后,已经添加到队列中但还尚未履行的义务不会履行,直到队列被复苏。

7. dispatch_suspend和dispatch_resume

诚如用法

dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_suspend(queue); //暂停队列queue
dispatch_resume(queue);  //恢复队列queue

使用场景
那种用法我还尚未品味过,可是里面有个需求留意的点。这三个函数不会影响到行列中一度履行的任务,队列暂停后,已经添加到队列中但还没有举行的天职不会举办,直到队列被还原。

8. dispatch_semaphore_signal

诚如用法

// dispatch_semaphore_signal有两类用法:a、解决同步问题;b、解决有限资源访问(资源为1,即互斥)问题。
// dispatch_semaphore_wait,若semaphore计数为0则等待,大于0则使其减1。
// dispatch_semaphore_signal使semaphore计数加1。

// a、同步问题:输出肯定为1、2、3。
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_semaphore_t semaphore1 = dispatch_semaphore_create(1);
dispatch_semaphore_t semaphore2 = dispatch_semaphore_create(0);
dispatch_semaphore_t semaphore3 = dispatch_semaphore_create(0);

dispatch_async(queue, ^{
    // 任务1
    dispatch_semaphore_wait(semaphore1, DISPATCH_TIME_FOREVER);
    NSLog(@"1\n");
    dispatch_semaphore_signal(semaphore2);
    dispatch_semaphore_signal(semaphore1);
});

dispatch_async(queue, ^{
    // 任务2
    dispatch_semaphore_wait(semaphore2, DISPATCH_TIME_FOREVER);
    NSLog(@"2\n");
    dispatch_semaphore_signal(semaphore3);
    dispatch_semaphore_signal(semaphore2);
});

dispatch_async(queue, ^{
    // 任务3
    dispatch_semaphore_wait(semaphore3, DISPATCH_TIME_FOREVER);
    NSLog(@"3\n");
    dispatch_semaphore_signal(semaphore3);
});

// b、有限资源访问问题:for循环看似能创建100个异步任务,实质由于信号限制,最多创建10个异步任务。
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_semaphore_t semaphore = dispatch_semaphore_create(10);
for (int i = 0; i < 100; i ++) {
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    dispatch_async(queue, ^{
    // 任务
    ...
    dispatch_semaphore_signal(semaphore);
    });
}

运用场景
实质上关于dispatch_semaphore_t,并没有观看太多使用和材料表明,我只可以参照自己对linux信号量的驾驭写了四个用法,经测试确实相似。那里,就不对一部分死锁难题开展研讨了。

8. dispatch_semaphore_signal

一般用法

// dispatch_semaphore_signal有两类用法:a、解决同步问题;b、解决有限资源访问(资源为1,即互斥)问题。
// dispatch_semaphore_wait,若semaphore计数为0则等待,大于0则使其减1。
// dispatch_semaphore_signal使semaphore计数加1。

// a、同步问题:输出肯定为1、2、3。
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_semaphore_t semaphore1 = dispatch_semaphore_create(1);
dispatch_semaphore_t semaphore2 = dispatch_semaphore_create(0);
dispatch_semaphore_t semaphore3 = dispatch_semaphore_create(0);

dispatch_async(queue, ^{
    // 任务1
    dispatch_semaphore_wait(semaphore1, DISPATCH_TIME_FOREVER);
    NSLog(@"1\n");
    dispatch_semaphore_signal(semaphore2);
    dispatch_semaphore_signal(semaphore1);
});

dispatch_async(queue, ^{
    // 任务2
    dispatch_semaphore_wait(semaphore2, DISPATCH_TIME_FOREVER);
    NSLog(@"2\n");
    dispatch_semaphore_signal(semaphore3);
    dispatch_semaphore_signal(semaphore2);
});

dispatch_async(queue, ^{
    // 任务3
    dispatch_semaphore_wait(semaphore3, DISPATCH_TIME_FOREVER);
    NSLog(@"3\n");
    dispatch_semaphore_signal(semaphore3);
});

// b、有限资源访问问题:for循环看似能创建100个异步任务,实质由于信号限制,最多创建10个异步任务。
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_semaphore_t semaphore = dispatch_semaphore_create(10);
for (int i = 0; i < 100; i ++) {
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    dispatch_async(queue, ^{
        // 任务
        ...
        dispatch_semaphore_signal(semaphore);
    });
}

采用场景
其实关于dispatch_semaphore_t,并没有观望太多应用和资料表达,我只能参照自己对linux信号量的精晓写了三个用法,经测试确实相似。这里,就不对一些死锁难题进行研商了。

9. dispatch_set_context、dispatch_get_context和dispatch_set_finalizer_f

貌似用法

// dispatch_set_context、dispatch_get_context是为了向队列中传递上下文context服务的。
// dispatch_set_finalizer_f相当于dispatch_object_t的析构函数。
// 因为context的数据不是foundation对象,所以arc不会自动回收,一般在dispatch_set_finalizer_f中手动回收,所以一般讲上述三个方法绑定使用。

- (void)test
{
    // 几种创建context的方式
    // a、用C语言的malloc创建context数据。
    // b、用C++的new创建类对象。
    // c、用Objective-C的对象,但是要用__bridge等关键字转为Core Foundation对象。

    dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
    if (queue) {
        // "123"即为传入的context
        dispatch_set_context(queue, "123");
        dispatch_set_finalizer_f(queue, &xigou);
    }
    dispatch_async(queue, ^{
        char *string = dispatch_get_context(queue);
        NSLog(@"%s", string);
    });
}

// 该函数会在dispatch_object_t销毁时调用。
void xigou(void *context)
{
    // 释放context的内存(对应上述abc)

    // a、CFRelease(context);
    // b、free(context);
    // c、delete context;
}

利用场景
dispatch_set_context能够为队列添加上下文数据,可是因为GCD是C语言接口形式的,所以其context参数类型是“void
*”。需利用上述abc三种办法创制context,并且一般结合dispatch_set_finalizer_f使用,回收context内存。

9. dispatch_set_context、dispatch_get_context和dispatch_set_finalizer_f

诚如用法

// dispatch_set_context、dispatch_get_context是为了向队列中传递上下文context服务的。
// dispatch_set_finalizer_f相当于dispatch_object_t的析构函数。
// 因为context的数据不是foundation对象,所以arc不会自动回收,一般在dispatch_set_finalizer_f中手动回收,所以一般讲上述三个方法绑定使用。

- (void)test
{
    // 几种创建context的方式
    // a、用C语言的malloc创建context数据。
    // b、用C++的new创建类对象。
    // c、用Objective-C的对象,但是要用__bridge等关键字转为Core Foundation对象。

    dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
    if (queue) {
        // "123"即为传入的context
        dispatch_set_context(queue, "123");
        dispatch_set_finalizer_f(queue, &xigou);
    }
    dispatch_async(queue, ^{
        char *string = dispatch_get_context(queue);
        NSLog(@"%s", string);
    });
}

// 该函数会在dispatch_object_t销毁时调用。
void xigou(void *context)
{
    // 释放context的内存(对应上述abc)

    // a、CFRelease(context);
    // b、free(context);
    // c、delete context;
}

运用场景
dispatch_set_context可以为队列添加上下文数据,不过因为GCD是C语言接口方式的,所以其context参数类型是“void
*”。需选用上述abc二种方式创设context,并且一般结合dispatch_set_finalizer_f使用,回收context内存。

四、内存和中卫

稍加提一下吗,因为部分人纠结于dispatch的内存难题。
内存

  • MRC:用dispatch_retain和dispatch_release管理dispatch_object_t内存。
  • ARC:ARC在编译时刻自动管理dispatch_object_t内存,使用retain和release会报错。

安全
dispatch_queue是线程安全的,你可以自由往里面添加任务。

四、内存和攀枝花

稍许提一下呢,因为有的人纠结于dispatch的内存难题。内存

  • MRC:用dispatch_retain和dispatch_release管理dispatch_object_t内存。
  • 澳门永利娱乐总站,ARC:ARC在编译时刻自动管理dispatch_object_t内存,使用retain和release会报错。

安全
dispatch_queue是线程安全的,你可以随便往里面添加职务。

五、拾遗

那边根本提一下GCD的有些坑和线程的有的标题。

五、拾遗

此处主要提一下GCD的片段坑和线程的一部分标题。

1. 死锁

dispatch_sync

// 假设这段代码执行于主队列
dispatch_queue_t serialQueue = dispatch_queue_create("serialQueue", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t mainQueue = dispatch_get_main_queue();

// 在主队列添加同步任务
dispatch_sync(mainQueue, ^{
    // 任务
    ...
});

// 在串行队列添加同步任务 
dispatch_sync(serialQueue, ^{
    // 任务
    ...
    dispatch_sync(serialQueue, ^{
        // 任务
        ...
    });
};

dispatch_apply

// 因为dispatch_apply会卡住当前线程,内部的dispatch_apply会等待外部,外部的等待内部,所以死锁。
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_apply(10, queue, ^(size_t) {
    // 任务
    ...
    dispatch_apply(10, queue, ^(size_t) {
        // 任务
        ...
    });
});

dispatch_barrier
dispatch_barrier_sync在串行队列和大局并行队列之中和dispatch_sync同样的效果,所以需考虑同dispatch_sync一样的死锁难点。

1. 死锁

dispatch_sync

// 假设这段代码执行于主队列
dispatch_queue_t serialQueue = dispatch_queue_create("serialQueue", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t mainQueue = dispatch_get_main_queue();

// 在主队列添加同步任务
dispatch_sync(mainQueue, ^{
    // 任务
    ...
});

// 在串行队列添加同步任务
dispatch_sync(serialQueue, ^{
    // 任务
    ...
    dispatch_sync(serialQueue, ^{
        // 任务
        ...
    });
};

dispatch_apply

// 因为dispatch_apply会卡住当前线程,内部的dispatch_apply会等待外部,外部的等待内部,所以死锁。
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_apply(10, queue, ^(size_t) {
    // 任务
    ...
    dispatch_apply(10, queue, ^(size_t) {
        // 任务
        ...
    });
});

dispatch_barrier
dispatch_barrier_sync在串行队列和大局并行队列之中和dispatch_sync同样的机能,所以需考虑同dispatch_sync一样的死锁难点。

2. dispatch_time_t

// dispatch_time_t一般在dispatch_after和dispatch_group_wait等方法里作为参数使用。这里最需要注意的是一些宏的含义。
// NSEC_PER_SEC,每秒有多少纳秒。
// USEC_PER_SEC,每秒有多少毫秒。
// NSEC_PER_USEC,每毫秒有多少纳秒。
// DISPATCH_TIME_NOW 从现在开始
// DISPATCH_TIME_FOREVE 永久

// time为1s的写法
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC);

2. dispatch_time_t

// dispatch_time_t一般在dispatch_after和dispatch_group_wait等方法里作为参数使用。这里最需要注意的是一些宏的含义。
// NSEC_PER_SEC,每秒有多少纳秒。
// USEC_PER_SEC,每秒有多少毫秒。
// NSEC_PER_USEC,每毫秒有多少纳秒。
// DISPATCH_TIME_NOW 从现在开始
// DISPATCH_TIME_FOREVE 永久

// time为1s的写法
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC);

3. GCD和线程的涉嫌

万一您是新手,GCD和线程暂时木有关系。
若果您是一把手,大家做恋人吗。

3. GCD和线程的关联

假设您是新手,GCD和线程暂时木有关系。
借使您是王牌,我们做恋人吧。

六、参考文献

1、https://developer.apple.com/library/mac/documentation/General/Conceptual/ConcurrencyProgrammingGuide/OperationQueues/OperationQueues.html\#//apple\_ref/doc/uid/TP40008091-CH102-SW2
2、https://developer.apple.com/library/ios/documentation/Performance/Reference/GCD\_libdispatch\_Ref/
3、http://tutuge.me/2015/04/03/something-about-gcd/
4、http://www.jianshu.com/p/85b75c7a6286
5、http://www.jianshu.com/p/d56064507fb8

六、参考文献

1、https://developer.apple.com/library/mac/documentation/General/Conceptual/ConcurrencyProgrammingGuide/OperationQueues/OperationQueues.html\#//apple\_ref/doc/uid/TP40008091-CH102-SW2
2、https://developer.apple.com/library/ios/documentation/Performance/Reference/GCD\_libdispatch\_Ref/
3、http://tutuge.me/2015/04/03/something-about-gcd/
4、http://www.jianshu.com/p/85b75c7a6286
5、http://www.jianshu.com/p/d56064507fb8

相关文章