正好说GCDMY_最为详解的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

相关文章