异步执行任务的技术之一,
用我们难以置信的非常简洁的记述方法,实现了极为复杂繁琐的多线程编程。
dispatch_async函数追加赋值变了queue的Dispatch Queue中,这样可以在另一线程中执行。
存在两种DIspatchQueue,一种事等待现在执行中处理的SerialDisPatch,一种是不等待现在执行中的ConcurrentDispatchQueue
SerialDisPatch 串行
ConcurrentDispatchQueue 并发
1.第一种方法 同归gcd的api生成
dispatch_queue_create
函数可生成DispatchQucue,
列如: dispatch_queue_t myser = dispatch_queue_create("com", NULL);NULL表示为串行SerialDisPatch
dispatch_queue_t myser = dispatch_queue_create("com", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(myser, ^{
});
DISPATCH_QUEUE_CONCURRENT表示为并发。
第二种方法获取系统标准提供的Dispatch Queue
Main Dispatch Queue 和Global Dispatch Queue(四个优先级)
Main Dispatch Queue是在主线程种执行的Dispatch Queue。 是串行
Global Dispatch Queue 是并发
获取方式
dispatch_queue_t aa = dispatch_get_main_queue();
dispatch_queue_t high= dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
dispatch_queue_t default1= dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_queue_t low= dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
dispatch_queue_t background= dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
Main Dispatch Queue 和Global Dispatch Queue并用
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
dispatch_async(dispatch_get_main_queue(), ^{
});
});
2.dispatch_set_target_queue 变更优先级
函数生成的Dispatch Queue 不管是串行还是并发都是使用Global Dispatch Queue默认优先级,而变更生成的Dispatch Queue的执行优先级要使用dispatch_set_target_queue函数
3.dispatch_after
假如3秒后执行 追加处理
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, ^{
NSLog(@"1");
});
dispatch_group_async(group, queue, ^{
NSLog(@"2");
});
dispatch_group_async(group, queue, ^{
NSLog(@"3");
});
dispatch_group_notify(group, queue, ^{
NSLog(@"4");
});
2014-03-30 01:01:30.394 arcc[2367:202023] 3
2014-03-30 01:01:30.394 arcc[2367:202022] 2
2014-03-30 01:01:30.394 arcc[2367:202021] 1
2014-03-30 01:01:30.396 arcc[2367:202021] 4
结果 前3个没有顺序,因为优先级别一样。dispatch_group_notify追加处理。还可以设置dispatch_group_wait等待,不过要设置等待时间。等待时间可以超时可以固定。
5.dispatch_barrier_async
访问数据库和文件时 可以并发的访问,不会发生数据竞争,前面都完成了才会追加执行以后的。
dispatch_queue_t queue = dispatch_queue_create("com", DISPATCH_QUEUE_CONCURRENT);(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
NSLog(@"1");
});
dispatch_async(queue, ^{
NSLog(@"2");
});
dispatch_barrier_async(queue, ^{
NSLog(@"3");
});
dispatch_async(queue, ^{
NSLog(@"4");
});
dispatch_async(queue, ^{
NSLog(@"5");
});
2014-03-30 01:13:11.787 arcc[2431:205805] 2
2014-03-30 01:13:11.787 arcc[2431:205806] 1
2014-03-30 01:13:11.788 arcc[2431:205806] 3
2014-03-30 01:13:11.788 arcc[2431:205806] 4
2014-03-30 01:13:11.789 arcc[2431:205806] 5
6.dispatch_sync
同步 ,
这个列子 死锁
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_sync(queue, ^{
NSLog(@"hello.死锁");
});
7.dispatch_apply
是dispatch_sync和Dispatch Group关联的api,
dispatch_apply会等待dispatch_apply块中的Dispatch Queue执行完后再执行后面的。
8.dispatch_suspend/dispatch_resume
挂起 /恢复
9.Dispatch Semaphore
dispatch_semaphore_wait 为0市等待,否则就减1
dispatch_semaphore_signal 信号量+1
进行细粒控制,怕程序异常结束
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
NSMutableArray * array = [[NSMutableArray alloc]init];
for (int i = 0; i< 100000; i++)
{
dispatch_async(queue, ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
[array addObject:[NSNumber numberWithInt:i]];
dispatch_semaphore_signal(semaphore);
});
}
10.dispatch_once
只执行一次
可以用在单利模式下面
static dispatch_once_t pred;
dispatch_once(&pred, ^{
//初始化
});
11.Dispatch I/O
大文件时,用它多线程读取
12、dispatch_block
在向队列中添加任务时,可以直接在对应的函数中添加 block。但是如果想对任务进行操作,比如监听任务、取消任务,就需要获取对应的 block。
dispatch_block_flags
DISPATCH_ENUM(dispatch_block_flags, unsigned long,
DISPATCH_BLOCK_BARRIER,
DISPATCH_BLOCK_DETACHED,
DISPATCH_BLOCK_ASSIGN_CURRENT,
DISPATCH_BLOCK_NO_QOS_CLASS,
DISPATCH_BLOCK_INHERIT_QOS_CLASS,
DISPATCH_BLOCK_ENFORCE_QOS_CLASS
);
dispatch_block_create创建
# 在该函数中,flags 参数用来设置 block 的标记,block 参数用来设置具体的任务。
# flags 的类型为 dispatch_block_flags_t 的枚举,用于设置 block 的标记,定义如下
dispatch_block_t dispatch_block_create(dispatch_block_flags_t flags, dispatch_block_t block);
# 相比于 dispatch_block_create 函数,这种方式在创建 block 的同时可以指定了相应的优先级。
# dispatch_qos_class_t 是 qos_class_t 的别名
dispatch_block_t
dispatch_block_create_with_qos_class(dispatch_block_flags_t flags,
dispatch_qos_class_t qos_class, int relative_priority,
dispatch_block_t block);
qos_class_t 是一种枚举,有以下类型:
QOS_CLASS_USER_INTERACTIVE:user interactive 等级表示任务需要被立即执行,用来在响应事件之后更新 UI,来提供好的用户体验。这个等级最好保持小规模。
QOS_CLASS_USER_INITIATED:user initiated 等级表示任务由 UI 发起异步执行。适用场景是需要及时结果同时又可以继续交互的时候。
QOS_CLASS_DEFAULT:default 默认优先级
QOS_CLASS_UTILITY:utility 等级表示需要长时间运行的任务,伴有用户可见进度指示器。经常会用来做计算,I/O,网络,持续的数据填充等任务。这个任务节能。
QOS_CLASS_BACKGROUND:background 等级表示用户不会察觉的任务,使用它来处理预加载,或者不需要用户交互和对时间不敏感的任务。
QOS_CLASS_UNSPECIFIED:unspecified 未指明
代码
dispatch_queue_t concurrentQuene = dispatch_queue_create("concurrentQuene", DISPATCH_QUEUE_CONCURRENT);
dispatch_block_t block = dispatch_block_create(0, ^{ NSLog(@"normal do some thing..."); });
dispatch_async(concurrentQuene, block);
dispatch_block_t qosBlock = dispatch_block_create_with_qos_class(0, QOS_CLASS_DEFAULT, 0, ^{
NSLog(@"qos do some thing...");
});
dispatch_async(concurrentQuene, qosBlock);
监听 block 执行结束 两种办法
有时我们需要等待特定的 block 执行完成之后,再去执行其他任务。有两种方法可以获取到指定 block 执行结束的时机。
dispatch_block_wait
long dispatch_block_wait(dispatch_block_t block, dispatch_time_t timeout);
#======================#======================#======================
# 如何使用
## 因为 dispatch_block_wait 会阻塞当前线程,所以不应该放在主线程中调用。创建一个新的并队列执行
dispatch_queue_t concurrentQuene = dispatch_queue_create("concurrentQuene", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(concurrentQuene, ^{
dispatch_queue_t allTasksQueue = dispatch_queue_create("allTasksQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_block_t block = dispatch_block_create(0, ^{
NSLog(@"开始执行");
[NSThread sleepForTimeInterval:3]; NSLog(@"结束执行");
});
dispatch_async(allTasksQueue, block);
// 等待时长,10s 之后超时
dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10 * NSEC_PER_SEC));
long resutl = dispatch_block_wait(block, timeout);
if (resutl == 0) {
NSLog(@"执行成功");
} else {
NSLog(@"执行超时");
}
});
dispatch_block_notify
第一个Block是操作的Block, 第二个Block是完成操作之后的Block
dispatch_block_notify(dispatch_block_t block, dispatch_queue_t queue,
dispatch_block_t notification_block);
#======================#======================#======================
NSLog(@"---- 开始设置任务 ----");
dispatch_queue_t serialQueue = dispatch_queue_create("com.fyf.serialqueue", DISPATCH_QUEUE_SERIAL);
// 耗时任务
dispatch_block_t taskBlock = dispatch_block_create(0, ^{
NSLog(@"开始耗时任务");
[NSThread sleepForTimeInterval:2.f];
NSLog(@"完成耗时任务");
});
dispatch_async(serialQueue, taskBlock);
// 更新 UI
dispatch_block_t refreshUI = dispatch_block_create(0, ^{ NSLog(@"更新 UI"); });
// 设置监听
dispatch_block_notify(taskBlock, dispatch_get_main_queue(), refreshUI);
NSLog(@"---- 完成设置任务 ----");
GCD的取消:dispatch_block_cancel
iOS8 后 GCD 支持对 dispatch block 的取消。
这个函数用异步的方式取消指定的 block。
取消操作使将来执行 dispatch block 立即返回,但是对已经在执行的 dispatch block 没有任何影响。
当一个 block 被取消时,它会立即释放捕获的资源。
如果要在一个 block 中对某些对象进行释放操作,在取消这个 block 的时候,需要确保内存不会泄漏。
# 取消当前已获取资源但尚未执行的Block
void dispatch_block_cancel(dispatch_block_t block);
# zero if not canceled. 下面的方法用来测试Block是否成功的取消了
long dispatch_block_testcancel(dispatch_block_t block);
#======================#======================#======================
dispatch_queue_t serialQueue = dispatch_queue_create("com.fyf.serialqueue", DISPATCH_QUEUE_SERIAL);
// 耗时任务
dispatch_block_t firstTaskBlock = dispatch_block_create(0, ^{
NSLog(@"开始第一个任务");
[NSThread sleepForTimeInterval:1.5f];
NSLog(@"结束第一个任务");
});
// 耗时任务
dispatch_block_t secTaskBlock = dispatch_block_create(0, ^{
NSLog(@"开始第二个任务");
[NSThread sleepForTimeInterval:2.f];
NSLog(@"结束第二个任务");
});
dispatch_async(serialQueue, firstTaskBlock);
dispatch_async(serialQueue, secTaskBlock);
// 等待 1s,让第一个任务开始运行
[NSThread sleepForTimeInterval:1];
dispatch_block_cancel(firstTaskBlock);
NSLog(@"尝试过取消第一个任务");
dispatch_block_cancel(secTaskBlock);
NSLog(@"尝试过取消第二个任务");
开始第一个任务
尝试过取消第一个任务
尝试过取消第二个任务
结束第一个任务
可以发现第二个任务可以顺利取消
dispatch source
dispatch源(dispatch source)和RunLoop源概念上有些类似的地方,而且使用起来更简单。要很好地理解dispatch源,其实把它看成一种特别的生产消费模式。dispatch源好比生产的数据,当有新数据时,会自动在dispatch指定的队列(即消费队列)上运行相应地block,生产和消费同步是dispatch源会自动管理的。
dispatch源的使用基本为以下步骤:
1. dispatch_source_t source = dispatch_source_create(dispatch_source_type, handler, mask, dispatch_queue);//创建dispatch源,这里使用加法来合并dispatch源数据,最后一个参数是指定dispatch队列
2. dispatch_source_set_event_handler(source, ^{ //设置响应dispatch源事件的block,在dispatch源指定的队列上运行
//可以通过dispatch_source_get_data(source)来得到dispatch源数据
});
3. dispatch_resume(source); //dispatch源创建后处于suspend状态,所以需要启动dispatch源
4. dispatch_source_merge_data(source, value); //合并dispatch源数据,在dispatch源的block中,dispatch_source_get_data(source)就会得到value。
是不是很简单?而且完全不必编写同步的代码。比如网络请求数据的模式,就可以这样来写:
dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_ADD, 0,0, dispatch_get_global_queue(0, 0));
dispatch_source_set_event_handler(source, ^{
dispatch_sync(dispatch_get_main_queue(), ^{
//更新UI
});
});
dispatch_resume(source);
dispatch_async(dispatch_get_global_queue(0, 0), ^{
//网络请求
dispatch_source_merge_data(source, 1); //通知队列
});
dispatch源还支持其它一些系统源,包括定时器、监控文件的读写、监控文件系统、监控信号或进程等,基本上调用的方式原理和上面相同,只是有可能是系统自动触发事件。比如dispatch定时器:
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
dispatch_source_set_timer(timer, dispatch_walltime(NULL, 0), 10*NSEC_PER_SEC, 1*NSEC_PER_SEC); //每10秒触发timer,误差1秒
dispatch_source_set_event_handler(timer, ^{
//定时处理
});
dispatch_resume(timer);
最后,dispatch源的其它一些函数大致罗列如下:
uintptr_t dispatch_source_get_handle(dispatch_source_t source); //得到dispatch源创建,即调用dispatch_source_create的第二个参数
unsignedlong dispatch_source_get_mask(dispatch_source_t source); //得到dispatch源创建,即调用dispatch_source_create的第三个参数
void dispatch_source_cancel(dispatch_source_t source); //取消dispatch源的事件处理--即不再调用block。如果调用dispatch_suspend只是暂停dispatch源。
long dispatch_source_testcancel(dispatch_source_t source); //检测是否dispatch源被取消,如果返回非0值则表明dispatch源已经被取消
void dispatch_source_set_cancel_handler(dispatch_source_t source, dispatch_block_t cancel_handler);//dispatch源取消时调用的block,一般用于关闭文件或socket等,释放相关资源
void dispatch_source_set_registration_handler(dispatch_source_t source, dispatch_block_tregistration_handler); //可用于设置dispatch源启动时调用block,调用完成后即释放这个block。也可在dispatch源运行当中随时调用这个函数。