-、GCD(Grand Central Dispatch)
OS X Snow Leopard / iOS4
异步执行任务的技术之一
简洁(封装)高效(C/系统级 XNU内核)
体验一下:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//新的线程中执行
NSLog(@"working");
dispatch_async(dispatch_get_main_queue(), ^{
//主线程中执行
NSLog(@"done");
});
});
二、多线程
多条无分叉路径 “上下文切换”
缺点:数据竞争 死锁 消耗大量内存
优点:防止阻塞主线程 提高执行效率 (NSRunLoop)
三、GCD的API
1、Dispatch Queue
1)定义
执行处理的等待队列(FIFO);
两类Dispatch Queue:Serial Dispatch Queue(串行队列) 和 Concurrent Dispatch Queue(并行队列)
两种队列的区别:
Serial Dispatch Queue:只创建一个线程;按照FIFO取Block分配给一个线程处理;有等待,顺序执行;
Concurrent Dispatch Queue:创建多个线程;按FIFO取Block分配给多个线程处理;无等待,同时执行;(注:线程树由XNU内核决定)
2)创建
//并行队列
dispatch_queue_t concurrent = dispatch_queue_create("com.study.gcd.CONCURRENT_QUEUE", DISPATCH_QUEUE_CONCURRENT);
//串行队列
dispatch_queue_t serial = dispatch_queue_create("com.study.gcd.SERIAL_QUEUE", DISPATCH_QUEUE_SERIAL/*NULL*/);
//多个串行队列可并行执行
//释放
//dispatch_release(serial);
//dispatch_retain(serial);
3)两个特殊的Dispatch Queue
dispatch_queue_t main = dispatch_get_main_queue();//串行队列 主线程队列(可用于更新界面)
dispatch_queue_t global = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);//并行队列(系统提供的全局队列)
优先级:DISPATCH_QUEUE_PRIORITY_HIGHT > DISPATCH_QUEUE_PRIORITY_DEFAULT > DISPATCH_QUEUE_PRIORITY_LOW > DISPATCH_QUEUE_PRIORITY_BACKGROUND
2、dispatch_set_target_queue
作用:变更dispatch_queue的执行优先级;设置dispatch_queue的执行层次;
dispatch_queue_create函数生成的dispatch queue(无论是串行还是并行)的优先级都与global dispatch queue的默认优先级相同;
变更方法(生成一个后台级的串行队列):
dispatch_queue_t background = dispatch_queue_create(“com.study.gcd.BACKGROUND_QUEUE”, NULL);
dispatch_queue_t global = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
dispatch_set_target_queue(background, global);
//可以通过指定dispatch_set_target_queue来使多个Serial Dispatch Queue按照层级串行执行
3、dispatch_after
作用:指定多长时间后追加block到queue
使用方法:
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 3ull * NSEC_PER_SEC);
dispatch_after(time, dispatch_get_main_queue(), ^{
NSLog(@“waited at least 3 seconds”);
});
//注意:并不是3秒后执行,而是三秒后加入队列,如果RunLoop每隔1/60s执行一次,那么上面的log最快3s执行,最慢(3+1/60)s执行
dispatch_time_t的获取:
//1 第一种方法 dispatch_time
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 1ull * NSEC_PER_SEC);
//2 第二种方法 dispatch_walltime//日期到dispatch time
dispatch_time_t get_time_by_date(NSDate * date) {
dispatch_time_t time;
NSTimeInterval interval = [date timeIntervalSince1970];
double second, nsecond;
nsecond = modf(interval, &second);
struct timespec spec;
spec.tv_sec = second;
spec.tv_nsec = nsecond;
time = dispatch_walltime(&spec, 0);
return time;
}
4、dispatch_group
作用:在执行完dispatch queue里所有的任务后执行结束处理或者在执行完所有的dispatch queue后执行结束处理
使用方法:
dispatch_queue_t global = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, global, ^{
NSLog(@"1---");
});
dispatch_group_async(group, global, ^{
NSLog(@"2---");
});
dispatch_group_async(group, global, ^{
NSLog(@"3---");
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"all done");
});
//上述的dispatch_group_notify可以换成dispatch_group_wait 所在线程会停止直到返回(阻塞)
//永久等待
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
//等待5s
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 5ull * NSEC_PER_SEC);
long result = dispatch_group_wait(group, time);
if(result == 0) {
//dispatch group全部处理
}else {
//dispatch group还有在执行
}
5、dispatch_barrier_async
作用:实现并行中的串行执行;如:前两个任务并行执行完后,开始执行第三个特殊任务,第三个特殊任务执行完后,并行执行第四第五个任务;
使用方法:
dispatch_queue_t concurrent = dispatch_queue_create("com.study.gcd.CONCURRENT_QUEUE", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(concurrent, ^{
NSLog(@"第一个任务");
[NSThread sleepForTimeInterval:3];
});
dispatch_async(concurrent, ^{
NSLog(@"第二个任务");
});
dispatch_barrier_sync(concurrent, ^{
NSLog(@"第三个特殊任务");
});
NSLog(@"hi");
dispatch_async(concurrent, ^{
NSLog(@"第四个任务");
});
dispatch_async(concurrent, ^{
NSLog(@"第五个任务");
});
注意:会阻塞当前线程
6、dispatch_sync/dispatch_async
dispatch_sync:同步任务 阻塞当前线程
dispatch_async:异步任务 不阻塞当前线程
死锁代码:
dispatch_sync(dispatch_get_main_queue(), ^{NSLog(@“死锁");});
7、dispatch_apply
作用:按指定次数将指定的block追加到dispatch queue,并等待全部执行结束(阻塞);
使用方法:
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_apply(10, queue, ^(size_t index) {
NSLog(@"%zu", index);
});
NSLog(@"dispatch apply done”);
推荐用法:
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
dispatch_apply(10/*[array count]*/, queue, ^(size_t index) {
NSLog(@"%zu", index);
});
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"update ui");
});
});
8、dispatch_suspend/dispatch_resume/dispatch_once
作用:挂起和恢复dispatch_queue;保证只执行一次;
dispatch_suspend(queue);
dispatch_resume(queue);
static dispatch_once_t once;
dispatch_once(&once, ^{
NSLog(@“只执行一次”);
});
9、dispatch_semaphore
作用:执行更细粒度的排他控制,也即是更精度的顺序控制(信号量机制);
使用方法:
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
NSMutableArray * array = [NSMutableArray array];
for (int i = 0; i< 100000; i++) {
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
[array addObject:[NSNumber numberWithInt:i]];
dispatch_semaphore_signal(semaphore);
}
10、dispatch_io
作用:分块读取大文件
使用方法:
dispatch_io 和 dispatch_data配合使用
四、GCD实现
1、用于管理block的c语言层实现的FIFO队列
2、Atomic函数中实现的用于拍他的轻量级信号
3、用于管理线程的C语言层实现的容器
4、内核XNU上的实现
dispatch_queue(libdispatch)
pthread_workqueue(libc)
workqueue(XNU内核)
block先加入到dispatch_continuation_t(上下文)然后再加入到dispatch queue
high priority -> pthread_workqueue -> WORKQUEUE_HIGH_PRIOQUEUE
default priority -> pthread_workqueue -> WORKQUEUE_DEFAULT_PRIOQUEUE
low priority -> pthread_workqueue -> WORKQUEUE_LOW_PRIOQUEUE
background priority -> pthread_workqueue -> WORKQUEUE_BG_PRIOQUEUE
high overcommit priority -> pthread_workqueue -> WORKQUEUE_HIGH_PRIOQUEUE
default overcommit priority -> pthread_workqueue -> WORKQUEUE_DEFAULT_PRIOQUEUE
low overcommit priority -> pthread_workqueue -> WORKQUEUE_LOW_PRIOQUEUE
background overcommit priority -> pthread_workqueue -> WORKQUEUE_BG_PRIOQUEUE
附:dispatch source
DISPATCH_SOURCE_TYPE_DATA_ADD
DISPATCH_SOURCE_TYPE_DATA_OR
DISPATCH_SOURCE_TYPE_MACH_SEND
DISPATCH_SOURCE_TYPE_MACH_RECV
DISPATCH_SOURCE_TYPE_PROC
DISPATCH_SOURCE_TYPE_READ
DISPATCH_SOURCE_TYPE_SIGNAL
DISPATCH_SOURCE_TYPE_TIMER
DISPATCH_SOURCE_TYPE_VNODE
DISPATCH_SOURCE_TYPE_WRITE
dispatch_queue没有取消的概念(不能取消除非自己处理)
dispatch_source可以取消