什么是GCD
以下摘自苹果的官方说明
Grand Central Dispatch
是异步执行任务的手段之一。一般将应用程序中记叙的线程管理的代码在系统级中实现。开发者只需要定义想要执行的任务并追加到适当的Dispatch Queue
中,GCD
就能生成必要的线程并计划执行任务。由于线程管理是作为系统的一部分来实现的,因此可以统一管理,也可执行任务,这样就比以前的线程更有效率。
应用程序在启动时候,通过最先执行的线程,即“主线程”来描绘用户界面、处理触摸屏幕事件等。如果在该主线程中进行长时间的处理,就会阻塞主线程的执行。在OS X
和iOS
的应用程序中,会妨碍主线程中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 Queue
和Concurrent 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
使用,这与NSObject
的performSelectorOnMainThread
实例方法相同 -
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 queue
和global 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 queue
即concurrent 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 queue
和dispatch_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/O
和dispatch data
使用GCD
要比使用pthread
和NSThread
这些一般的多线程编程API
更好,如果使用GCD
就不必编写为操作线程反复出现的类似的固定源代码片段,而可以在线程中集中实现处理内容
GCD
的API
全部在包含在libdispatch
库中的C语言函数
dispatch queue
通过结构体和链表,被实现为FIFO
队列,FIFO
队列管理是通过dispatch_async
等函数所追加的block
不定期更新 不合适的地方 还请指点~ 感激不尽
o(* ̄3 ̄)o