GCD全称为Grand Central Dispatch,是libdispatch的市场名称,而libdispatch是Apple的一个库,其为并发代码在iOS和OS X的多核硬件上执行提供支持。确切地说GCD是一套低层级的C API,通过 GCD,开发者只需要向队列中添加一段代码块(block或C函数指针),而不需要直接和线程打交道。GCD在后端管理着一个线程池,它不仅决定着你的代码块将在哪个线程被执行,还根据可用的系统资源对这些线程进行管理。这样通过GCD来管理线程,从而解决线程被创建的问题
GCD 核心概念
GCD编程的核心就是dispatch队列,dispatch block的执行最终都会放进某个队列中去进行。
1、GCD中的队列类型
1、The main queue(主线程串行队列): 与主线程功能相同,提交至Main queue的任务会在主线程中执行,Main queue 可以通过dispatch_get_main_queue()来获取。
2、Global queue(全局并发队列): 全局并发队列由整个进程共享,有高、中(默认)、低、后台四个优先级别。Global queue 可以通过调用dispatch_get_global_queue函数来获取(可以设置优先级)
3、Custom queue (自定义队列): 可以为串行,也可以为并发。Custom queue 可以通过dispatch_queue_create()来获取;
4、Group queue (队列组):将多线程进行分组,最大的好处是可获知所有线程的完成情况。Group queue 可以通过调用dispatch_group_create()来获取,通过dispatch_group_notify,可以直接监听组里所有线程完成情况。
2、GCD相关概念
1、Dispatch Objects
尽管GCD是纯C语言的,但它被组建成面向对象的风格。GCD对象被称为dispatch object, 所有的dispatch objects都是OC对象.,就如其他OC对象一样,当开启了ARC(automatic reference counting)时,dispatch objects的retain和release都会自动执行。而如果是MRC的话,dispatch objects会使用dispatch_retain和dispatch_release这两个方法来控制引用计数。
2、Serial & Concurrent
串行任务就是每次只有一个任务被执行,并发任务就是在同一时间可以有多个任务被执行。
3、Synchronous & Asynchronous
同步函数意思是在完成了它预定的任务后才返回,在任务执行时会阻塞当前线程。而异步函数则是任务会完成但不会等它完成,所以异步函数不会阻塞当前线程,会继续去执行下一个函数。
4、Concurrency & Parallelism
并发的意思就是同时运行多个任务。这些任务可能是以在单核 CPU 上以分时(时间共享)的形式同时运行,也可能是在多核 CPU 上以真正的并行方式来运行。然后为了使单核设备也能实现这一点,并发任务必须先运行一个线程,执行一个上下文切换,然后运行另一个线程或进程。并行则是真正意思上的多任务同时运行。
5、Context Switch
Context Switch即上下文切换,一个上下文切换指当你在单个进程里切换执行不同的线程时存储与恢复执行状态的过程。这个过程在编写多任务应用时很普遍,但会带来一些额外的开销。
6、Dispatch Queues
GCD dispatch queues是一个强大的执行多任务的工具。Dispatch queue是一个对象,它可以接受任务,并将任务以先进先出(FIFO)的顺序来执行。Dispatch queue可以并发的或串行的执行任意一个代码块,而且并发任务会像NSOperationQueue那样基于系统负载来合适地并发进行,串行队列同一时间则只执行单一任务。Dispatch queues内部使用的是线程,GCD 管理这些线程,并且使用Dispatch queues的时候,我们都不需要自己创建线程。Dispatch queues相对于和线程直接通信的代码优势是:Dispatch queues使用起来特别方便,执行任务更加有效率。
GCD 简单用法
1、获取主队列
//获取主队列
dispatch_queue_t main_queue = dispatch_get_main_queue();
//主队列是串行队列;里面的任务会串行执行
dispatch_async(main_queue, ^{
NSLog(@"%@",[NSThread currentThread]);
for (int i=0; i<3; i++) {
NSLog(@"A:%d",i);
sleep(1);
}
});
dispatch_async(main_queue, ^{
NSLog(@"%@",[NSThread currentThread]);
for (int i=0; i<3; i++) {
NSLog(@"B:%d",i);
sleep(1);
}
});
2、获取全局队列
//获取全局队列
dispatch_queue_t global_queue=dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//全局队列中添加两个任务,全局队列是并发队列。
dispatch_async(global_queue, ^{
NSLog(@"%@",[NSThread currentThread]);
for (int i=0; i<3; i++) {
NSLog(@"C:%d",i);
sleep(1);
}
});
dispatch_async(global_queue, ^{
NSLog(@"%@",[NSThread currentThread]);
for (int i=0; i<3; i++) {
NSLog(@"D:%d",i);
sleep(1);
}
});
3、自定义串行队列
//创建串行队列
dispatch_queue_t serial_queue = dispatch_queue_create("my_serial_queue", DISPATCH_QUEUE_SERIAL);
NSLog(@"%@",serial_queue.description);
//添加任务
dispatch_async(serial_queue, ^{
NSLog(@"%@",[NSThread currentThread]);
for (int i=0; i<3; i++) {
NSLog(@"E:%d",i);
sleep(1);
}
});
dispatch_async(serial_queue, ^{
NSLog(@"%@",[NSThread currentThread]);
for (int i=0; i<3; i++) {
NSLog(@"F:%d",i);
sleep(1);
}
});
4、自定义并发队列
//创建一个并发队列
dispatch_queue_t concurrent_queue = dispatch_queue_create("my_concurrent_queue", DISPATCH_QUEUE_CONCURRENT);
//添加并发任务
dispatch_async(concurrent_queue, ^{
NSLog(@"%@",[NSThread currentThread]);
for (int i=0; i<3; i++) {
NSLog(@"G:%d",i);
sleep(1);
}
});
dispatch_async(concurrent_queue, ^{
NSLog(@"%@",[NSThread currentThread]);
for (int i=0; i<3; i++) {
NSLog(@"H:%d",i);
sleep(1);
}
});
5、延迟执行一个任务
//延迟多少秒
double delaySeconds=5;
//创建时间
dispatch_time_t delay_time = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delaySeconds * NSEC_PER_SEC));
//执行延迟任务
dispatch_after(delay_time, dispatch_get_main_queue(), ^{
NSLog(@"hello world");
});
6、调度监听任务完成
//获取并发队列
dispatch_queue_t global_queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//创建调度组
dispatch_group_t one_group = dispatch_group_create();
//通过调度组发起并发任务
dispatch_group_async(one_group, global_queue, ^{
for (int i=0; i<3; i++) {
NSLog(@"I:%d",i);
sleep(1);
}
});
dispatch_group_async(one_group, global_queue, ^{
for (int i=0; i<4; i++) {
NSLog(@"J:%d",i);
sleep(1);
}
});
dispatch_group_async(one_group, global_queue, ^{
for (int i=0; i<5; i++) {
NSLog(@"K:%d",i);
sleep(1);
}
});
//调度组完成任务通知
dispatch_group_notify(one_group, dispatch_get_main_queue(), ^{
NSLog(@"one_group完成任务");
});
}
7、调度等待任务完成
//获取并发队列
dispatch_queue_t global_queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//创建调度组
dispatch_group_t one_group = dispatch_group_create();
//进入调度组
dispatch_group_enter(one_group);
//发起并发任务
dispatch_async(global_queue, ^{
for (int i=0; i<3; i++) {
NSLog(@"L:%d",i);
sleep(1);
}
});
dispatch_async(global_queue, ^{
for (int i=0; i<4; i++) {
NSLog(@"M:%d",i);
sleep(1);
}
});
dispatch_async(global_queue, ^{
for (int i=0; i<5; i++) {
NSLog(@"N:%d",i);
sleep(1);
}
//离开调度组
dispatch_group_leave(one_group);
});
//调度组等待:等待过程中,下面的代码不会被执行
dispatch_group_wait(one_group, DISPATCH_TIME_FOREVER);
NSLog(@"等到了!");
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等等。
2、dispatch_after
// NSEC_PER_SEC,每秒有多少纳秒。
// USEC_PER_SEC,每秒有多少毫秒。
// NSEC_PER_USEC,每毫秒有多少纳秒。
// DISPATCH_TIME_NOW 从现在开始
// DISPATCH_TIME_FOREVE 永久
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加载结束延迟执行一个动画等等。
3、dispatch_once
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// 只执行一次的任务
...
});
可以使用其创建一个单例
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
});
dispatch_group_notify(group, mainQueue, ^{
// 任务完成后,在主队列中做一些操作
...
});
上述的一种方式,可以适用于自己维护的一些异步任务的同步问题
5、 dispatch_semaphore_signal(信号量)
//传递的参数是信号量最初值,下面例子的信号量最初值是1
dispatch_semaphore_t signal = dispatch_semaphore_create(1);
dispatch_time_t overTime = dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 当信号量是0的时候,dispatch_semaphore_wait(signal, overTime);这句代码会一直等待直到overTime超时.
//这里信号量是1 所以不会在这里发生等待.
dispatch_semaphore_wait(signal, overTime);
NSLog(@"需要线程同步的操作1 开始");
sleep(2);
NSLog(@"需要线程同步的操作1 结束");
long signalValue = dispatch_semaphore_signal(signal);//这句代码会使信号值 增加1
//并且会唤醒一个线程去开始继续工作,如果唤醒成功,那么返回一个非零的数,如果没有唤醒,那么返回 0
NSLog(@"%ld",signalValue);
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(1);
dispatch_semaphore_wait(signal, overTime);
NSLog(@"需要线程同步的操作2");
dispatch_semaphore_signal(signal);
long signalValue = dispatch_semaphore_signal(signal);
NSLog(@"%ld",signalValue);
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(1);
dispatch_semaphore_wait(signal, overTime);
NSLog(@"需要线程同步的操作3");
dispatch_semaphore_signal(signal);
long signalValue = dispatch_semaphore_signal(signal);
NSLog(@"%ld",signalValue);
});
利用 dispatch_semaphore_t signal 组织一个并发数是10 的一个多线程工作队列.
dispatch_group_t group = dispatch_group_create();
dispatch_semaphore_t semaphore = dispatch_semaphore_create(10);
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
for (int i = 0; i < 100; i++)
{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
//注意这里信号量从10开始递减,并不会阻塞循环.循环10次,递减到0的时候,开始阻塞.
NSLog(@"-------");
dispatch_group_async(group, queue, ^{
NSLog(@"%i",i);
sleep(1);
dispatch_semaphore_signal(semaphore);
});//创建一个新线程,并在线程结束后,发送信号量,通知阻塞的循环继续创建新线程.
}
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
如此循环就形成了对并发的控制,如上就是一个并发数为10的一个线程队列。
实战:利用 dispatch_semaphore_t signal 组织一个生产消费模式
__block int product = 0;
dispatch_semaphore_t sem = dispatch_semaphore_create(0);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ //消费者队列
while (1) {
if(!dispatch_semaphore_wait(sem, dispatch_time(DISPATCH_TIME_NOW, DISPATCH_TIME_FOREVER))){
////非 0的时候,就是成功的timeout了,这里判断就是没有timeout 成功的时候是 0
NSLog(@"消费%d产品",product);
product--;
};
}
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ //生产者队列
while (1) {
sleep(1); //wait for a while
product++;
NSLog(@"生产%d产品",product);
dispatch_semaphore_signal(sem);
}
});
6、 dispatch_suspend和dispatch_resume
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_suspend(queue); //暂停队列queue
dispatch_resume(queue); //恢复队列queue