(๑•̀ㅂ•́)و✧ GCD API

什么是GCD
以下摘自苹果的官方说明
Grand Central Dispatch是异步执行任务的手段之一。一般将应用程序中记叙的线程管理的代码在系统级中实现。开发者只需要定义想要执行的任务并追加到适当的Dispatch Queue中,GCD就能生成必要的线程并计划执行任务。由于线程管理是作为系统的一部分来实现的,因此可以统一管理,也可执行任务,这样就比以前的线程更有效率。


应用程序在启动时候,通过最先执行的线程,即“主线程”来描绘用户界面、处理触摸屏幕事件等。如果在该主线程中进行长时间的处理,就会阻塞主线程的执行。在OS XiOS的应用程序中,会妨碍主线程中Runloop的主循环的执行,从而导致不能更新用户界面、应用程序的画面长时间停滞等问题
这就是长时间的处理不在主线程中执行而在其他线程中执行的原因
使用多线程编程,在执行长时间的处理时仍可保证用户界面的响应性能
GCD大大简化了偏于复杂的多线程编程的源代码~


本文介绍了一些常用的API


1. Dispatch Queue

Dispatch Queue是什么呐?
如其名,是执行处理的等待队列。应用程序编程人员通过dispatch_async函数等API,在Block语法中记叙想要执行的处理并将其追加到Dispatch Queue中。Dispatch Queue按照追加的顺序(先进先出FIFO,first-in-first-out)执行处理。
在执行处理的时候,存在两种Dispatch Queue
一种是等待现在执行中处理的Serial Dispatch Queue,在不能改变执行的处理顺序或者不想并行执行多个处理时候使用
另一种是不等待现在执行中处理的Concurrent Dispatch Queue,执行顺序会根据处理的内容和系统状态发生改变

2. dispatch_queue_create

参数1:指定dispatch queue的名称,该名称也会出现在应用程序crash时所生成的CrashLog中,如果怕麻烦可以设置为NULL
参数2:如果需要生成serial dispatch queue时可以指定为NULL,如果要生成concurrent dispatch queue时可指定为DISPATCH_QUEUE_CONCURRENT
返回值为dispatch_queue_t类型

API_AVAILABLE(macos(10.6), ios(4.0))
DISPATCH_EXPORT DISPATCH_MALLOC DISPATCH_RETURNS_RETAINED DISPATCH_WARN_RESULT
DISPATCH_NOTHROW
dispatch_queue_t
dispatch_queue_create(const char *_Nullable label,
        dispatch_queue_attr_t _Nullable attr);

dispatch_queue_create函数可以生成Dispatch Queue
虽然Serial Dispatch QueueConcurrent Dispatch Queue受到系统资源的限制,但是使用dispatch_queue_create函数可以生成任意数量的Dispatch Queue
当生成多个Serial Dispatch Queue时候,各个serial dispatch queue会并行执行,虽然在一个serial dispatch queue中同时只能执行一个追加处理,但是如果将处理分别追加到多个serial dispatch queue中,每个serial dispatch queue执行一个,即可同时执行多个处理,也即多个serial dispatch queue可以并行执行
当然如果过多的使用多线程,就会消耗大量的内存,引起大量的上下文切换,大幅度将对系统的响应性能

3. Main Dispatch Queue / Global Dispatch Queue

系统标准提供的有 dispatch queue,不用特意生成dispatch queue

  • main dispatch queue
    是在主线程中执行的dispatch queue,因为主线程只有一个,所以main dispatch queue自然就是serial dispatch queue。追加到main dispatch queue的处理在主线程的runloop中执行。由于在主线程中执行,因此要将用户界面的更新等一些必须要在主线程中执行的处理追加到main dispatch queue使用,这与NSObjectperformSelectorOnMainThread实例方法相同
  • global dispatch queue
    是所有应用程序都可以使用的concurrent dispatch queue,没有必要使用dispatch_queue_create函数诸葛生成concurrent dispatch queue,只要获取到global dispatch queue使用即可
    global有四个执行优先级:高优先级DISPATCH_QUEUE_PRIORITY_HIGH,默认优先级DISPATCH_QUEUE_PRIORITY_DEFAULT,低优先级DISPATCH_QUEUE_PRIORITY_LOW,后台优先级DISPATCH_QUEUE_PRIORITY_BACKGROUND
    在像global dispatch queue追加处理时候,应选择与处理内容对应的执行优先级
// 获取 main dispatch queue
dispatch_queue_t main_dispatchqueue = dispatch_get_main_queue();
// 获取 global dispatch queue 高优先级 DISPATCH_QUEUE_PRIORITY_HIGH
dispatch_queue_t high_global_dispatchqueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
// 获取 global dispatch queue 默认先级 DISPATCH_QUEUE_PRIORITY_DEFAULT
dispatch_queue_t default_global_dispatchqueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 获取 global dispatch queue 低先级 DISPATCH_QUEUE_PRIORITY_LOW
dispatch_queue_t low_global_dispatchqueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
// 获取 global dispatch queue 后台先级 DISPATCH_QUEUE_PRIORITY_BACKGROUND
dispatch_queue_t background_global_dispatchqueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);

3. dispatch_set_target_queue

参数1:指定要变更优先级的dispatch queue,不能指定为main dispatch queueglobal dispatch queue,因为这两种不可使用该函数指定
参数2:指定与要使用的执行优先级 相同优先级的global dispatch queue

API_AVAILABLE(macos(10.6), ios(4.0))
DISPATCH_EXPORT DISPATCH_NOTHROW
void
dispatch_set_target_queue(dispatch_object_t object,
        dispatch_queue_t _Nullable queue);

dispatch_queue_create函数生成的dispatch queue不管是serial dispatch queue还是concurrent dispatch queue,使用的都是与global dispatch queue默认优先级 相同的 执行优先级线程
如果想要变更生成的dispatch queue的执行优先级,则要使用该函数
eg:

dispatch_queue_t ff_queue = dispatch_queue_create("fengfeng", NULL);
dispatch_set_target_queue(ff_queue, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0));

4. dispatch_after

参数1:指定时间用dispatch_time_t类型,该值可以使用dispatch_time函数或者dispatch_walltime函数
参数2:指定要追加处理的dispatch queue
参数3:指定要继续执行处理的block

#ifdef __BLOCKS__
API_AVAILABLE(macos(10.6), ios(4.0))
DISPATCH_EXPORT DISPATCH_NONNULL2 DISPATCH_NONNULL3 DISPATCH_NOTHROW
void
dispatch_after(dispatch_time_t when,
    dispatch_queue_t queue,
    dispatch_block_t block);
#endif

在指定时间后执行处理,需要注意的是,dispatch_after函数并不是在指定时间后执行处理,而只是在到指定时间的时候追加处理到dispatch queue
虽然在有严格时间要求的情况下使用会出现问题,但是如果只是大致延迟执行处理的情况还是很有效

dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 100ull * NSEC_PER_SEC);
dispatch_after(time, dispatch_get_main_queue(), ^{
    NSLog(@"\n-----> waited at least 100 seconds");
});

5. Dispatch Group

在追加到dispatch queue中的多个处理全部结束后想执行结束处理的时候
如果只是使用了一个serial dispatch queue时候,只要将想执行的处理全部追加到该serial dispatch queue中,并且在最后追加结束处理,即可
但是如果是使用concurrent dispatch queue时或者同时使用多个dispatch queue时,就不好处理
在这种情况下,可以使用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, ^{NSLog(@"\n-----> block00");});
dispatch_group_async(group, queue, ^{NSLog(@"\n-----> block01");});
dispatch_group_async(group, queue, ^{NSLog(@"\n-----> block02");});

dispatch_group_notify(group, dispatch_get_main_queue(), ^{
    NSLog(@"\n-----> finish");
});

// 或者使用
// DISPATCH_TIME_FOREVER 永久等待,即只要group中没有执行完毕,就一直等待不可取消
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);

因为向global dispatch queueconcurrent dispatch queue追加处理的时候,多个线程并行执行,所以追加处理的执行顺序不定,但是finish一定是最后执行
无论像什么样的dispatch queue中追加处理,使用dispatch group都可以监听这些处理的结束,一旦检测到所有处理执行结束,就可以将结束的处理追加到dispatch queue

6. dispatch_group_notify

参数1:要监听的dispatch group
参数2:指定的queue

#ifdef __BLOCKS__
API_AVAILABLE(macos(10.6), ios(4.0))
DISPATCH_EXPORT DISPATCH_NONNULL_ALL DISPATCH_NOTHROW
void
dispatch_group_notify(dispatch_group_t group,
    dispatch_queue_t queue,
    dispatch_block_t block);
#endif

在追加到group的全部处理执行结束的时候,函数会将执行的block追加到指定的queue

7. dispatch_group_wait

API_AVAILABLE(macos(10.6), ios(4.0))
DISPATCH_EXPORT DISPATCH_NONNULL_ALL DISPATCH_NOTHROW
long
dispatch_group_wait(dispatch_group_t group, dispatch_time_t timeout);

如果函数返回值不为0,就意味着,虽然过了指定等待时间,但是属于group的某个处理还在进行。如果返回为0,那么全部处理执行结束
当指定时间为DISPATCH_TIME_FOREVER,返回结果一定为0

dispatch_time_t time2 = dispatch_time(DISPATCH_TIME_NOW, 1ull * NSEC_PER_SEC);
long result = dispatch_group_wait(group, time2);
if (result == 0) {
    // 属于 group 的全部处理执行完毕
} else {
    // 属于 group 的某一个处理还在执行中
}

这里的wait指的是,一旦调用dispatch_group_wait函数,该函数就处于调用的状态而不返回,即执行该函数的当前线程停止,在经过指定的时间 或者 属于指定group的处理全部执行结束之前,执行该函数的线程停止

8. dispatch_barrier_async

#ifdef __BLOCKS__
API_AVAILABLE(macos(10.7), ios(4.3))
DISPATCH_EXPORT DISPATCH_NONNULL_ALL DISPATCH_NOTHROW
void
dispatch_barrier_async(dispatch_queue_t queue, dispatch_block_t block);
#endif

该函数会等待追加到concurrent dispatch queue上的并行执行的处理部分全部结束之后,再将指定的的处理追加到该concurrent dispatch queue中,然后在由dispatch_barrier_async函数追加的处理执行完毕后 该queue才恢复为一般的动作,继续处理追加到该queue的处理又继续执行
使用concurrent dispatch queuedispatch_barrier_async函数可以实现高效率的数据库访问和文件访问

void (^block0_for_reading)() = ^() {
    // reading
};
dispatch_queue_t queue = dispatch_queue_create("fengfeng", DISPATCH_QUEUE_CONCURRENT);
// 读操作
dispatch_async(queue, block0_for_reading);
dispatch_async(queue, block1_for_reading);
dispatch_async(queue, block2_for_reading);
// 写入操作
dispatch_barrier_async(queue, block0_for_writing);
// 上面的写入操作完成后,继续执行下面的读操作
dispatch_async(queue, block3_for_reading);
dispatch_async(queue, block4_for_reading);
dispatch_async(queue, block5_for_reading);

9. dispatch_async

#ifdef __BLOCKS__
API_AVAILABLE(macos(10.6), ios(4.0))
DISPATCH_EXPORT DISPATCH_NONNULL_ALL DISPATCH_NOTHROW
void
dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
#endif

dispatch_async函数的async为异步的asynchronous,就是将指定的block非同步地加入指定的dispatch queue
dispatch_async不做任何的等待

10. dispatch_sync

#ifdef __BLOCKS__
API_AVAILABLE(macos(10.6), ios(4.0))
DISPATCH_EXPORT DISPATCH_NONNULL_ALL DISPATCH_NOTHROW
void
dispatch_sync(dispatch_queue_t queue, DISPATCH_NOESCAPE dispatch_block_t block);
#endif

dispatch_sync函数的sync为同步的synchronous,将指定的block同步地追加到指定的dispatch queue
在追加的block结束之前,dispatch_sync函数会一直等待,即当前线程停止

11. dispatch_apply

参数1:重复次数
参数2:追加对象的queue
参数3:追加的处理,为带有参数的block,这是为了按照第一个参数重复追加block,并区分各个block而使用

#ifdef __BLOCKS__
API_AVAILABLE(macos(10.6), ios(4.0))
DISPATCH_EXPORT DISPATCH_NONNULL3 DISPATCH_NOTHROW
void
dispatch_apply(size_t iterations, dispatch_queue_t queue,
        DISPATCH_NOESCAPE void (^block)(size_t));
#endif

dispatch_apply函数是dispatch_sync函数和dispatch group的关联api
该函数按指定的次数将指定的block追加到指定的dispatch queue中,并等待全部处理结束
由于dispatch_apply函数也与dispatch_sync函数相同,会等待处理执行结束,因此推荐在dispatch_async函数中异步地执行dispatch_apply函数

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_apply(10, queue, ^(size_t index) {
    NSLog(@"\n-----> %zu", index);
});
NSLog(@"\n-----> done");

12. dispatch_suspend / dispatch_resume

dispatch_suspend函数 挂起指定的dispatch queue

API_AVAILABLE(macos(10.6), ios(4.0))
DISPATCH_EXPORT DISPATCH_NONNULL_ALL DISPATCH_NOTHROW
void
dispatch_suspend(dispatch_object_t object);

dispatch_resume函数 恢复指定的dispatch queue

API_AVAILABLE(macos(10.6), ios(4.0))
DISPATCH_EXPORT DISPATCH_NONNULL_ALL DISPATCH_NOTHROW
void
dispatch_resume(dispatch_object_t object);

这些函数对已经执行的处理没有影响
挂起后,追加到dispatch queue中但尚未执行的处理在此之后停止执行,而恢复,则使得这些处理能够继续执行

13. Dispatch Semaphore

dispatch semaphore是持有计数的信号,该计数是多线程编程中的技术类型信号。类似于过马路时候用到的手旗,可通过时候举起手旗,不可时放下手旗。而在dispatch semaphore中,使用计数来实现该功能,计数为0时候等待,计数为1或者大于1时候,减去1而不等待

14. dispatch_once

static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
    // code to be executed once
});

dispatch_once函数是保证应用程序执行中只执行一次指定处理的api,这种方式即使在多线程环境下执行也可保证安全
可用于单例模式

15. Dispatch I/O

在读取较大的文件时候,如果将文件分成合适的大小并使用global dispatch queue并列读取的话,应该会比一般的读取速度快不少
现在的 输入/输出 硬件已经可以做到一次使用多个线程更快地并列读取了
能实现这一功能的就是 dispatch I/Odispatch data


使用GCD要比使用pthreadNSThread这些一般的多线程编程API更好,如果使用GCD就不必编写为操作线程反复出现的类似的固定源代码片段,而可以在线程中集中实现处理内容
GCDAPI全部在包含在libdispatch库中的C语言函数
dispatch queue通过结构体和链表,被实现为FIFO队列,FIFO队列管理是通过dispatch_async等函数所追加的block


不定期更新 不合适的地方 还请指点~ 感激不尽

o(* ̄3 ̄)o

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,732评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,496评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,264评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,807评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,806评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,675评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,029评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,683评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,704评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,666评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,773评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,413评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,016评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,204评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,083评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,503评论 2 343

推荐阅读更多精彩内容