dispatch_queue_create
OS X和iOS的核心XNU内核在发生操作系统事件时(如每隔一定时间,唤起系统调用等情况)会切换执行路径。执行中路径的状态,例如CPU的寄存器等信息保存在各自路径专用的内存块中,从切换目标路径专用的内存块中,复原CPU寄存器等信息,继续执行切换路径的CPU命令列,这被称为“上下文切换”。
Dispatch Quene 是执行处理的等待队列,按照追加的顺序(FIFO)执行处理。Serial Dispatch Quene等待现在执行中处理结束。Concurrent Dispatch Quene不等待现在执行中处理结束。并行执行的处理数量取决于当前系统的状态,即iOS和OSX基于Dispatch Quene中的处理数、CPU核数以及CPU负荷等当前系统的状态来决定并行执行的处理数。
当生成多个Serial Dispatch Quene时,各个Serial Dispatch Quene将并行执行。如果过多使用多线程,就会消耗大量内存,引起大量的上下文切换,大幅度降低系统的相应性能。在多个线程更新相同资源导致数据竞争时使用Serial Dispatch Quene。当想并行执行不发生数据竞争等问题的处理时,使用Concurrent Dispatch Quene,而且不管生成多少,由于XNU内核只使用有效管理的线程,因为不会发生Serial Dispatch Quene的那些问题。
dispatch_queue_t mySerialDispatchQuene = dispatch_queue_create("com.example.gcd.mySerialDispatchQuene", NULL);
dispatch_queue_t myConcurrentDispatchQuene = dispatch_queue_create("com.example.gcd.myConcurrentDispatchQuene", DISPATCH_QUEUE_CONCURRENT);
/// 该Block持有Dispatch Quene
dispatch_async(myConcurrentDispatchQuene, ^{
NSLog(@"block on myConcurrentDispatchQuene");
});
/// Mountain Lion and iOS 6.0, ARC will now manage dispatch queues and other GCD types
// dispatch_release(myConcurrentDispatchQuene);
// dispatch_retain(mySerialDispatchQuene);
dispatch_get_main_queue dispatch_get_global_queue
XNU内核用于Global Dispatch Quene 的线程并不能保证实时性,因此执行优先级只能大致的判断。
对Global Dispatch Quene和 Main Dispatch Quene执行dispatch_release和dispatch_retain函数不会引起任何变化。
dispatch_queue_t mainDispatchQuene = dispatch_get_main_queue();
/// 高优先级、默认优先级、低优先级、后台优先级Global Dispatch Quene的获取
dispatch_queue_t globalDispatchQueneHigh = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
dispatch_queue_t globalDispatchQueneDefault = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_queue_t globalDispatchQueneLow = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
dispatch_queue_t globalDispatchQueneBackground = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
dispatch_set_target_queue
dispatch_queue_create函数生成的Dispatch Quene不管是Serial Dispatch Quene还是Concurrent Dispatch Quene,都使用与默认优先级Global Dispatch Quene相同执行优先级的线程。dispatch_set_target_queue可变更执行优先级。将Dispatch Quene指定为dispatch_set_target_queue的参数,不仅可以变更Dispatch Quene的优先级,还可以作为Dispatch Quene的执行阶层。如果在多个Serial Dispatch Quene中用dispatch_set_target_queue函数指定目标为某一个dispatch_set_target_queue,那么原本应并行执行的多个Serial Dispatch Quene,在目标Serial Dispatch Quene上只能同时执行一个处理。
dispatch_set_target_queue(mySerialDispatchQuene, globalDispatchQueneBackground);
dispatch_after
dispatch_after函数并不是在指定时间后执行处理,而是在指定时间追加处理到Dispatch Quene。因为Main Dispatch Quene在主线程的Runloop中执行,所以在 比如每隔1/60秒执行的Runloop中,Block最快3S后执行,最慢在3S+1/60S后执行,并且在Main Dispatch Quene有大量处理追加或主线程的处理本身有延迟是,这个时间会更长。
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 3ull * NSEC_PER_SEC);
dispatch_after(time, dispatch_get_main_queue(), ^{
NSLog(@"waiter at least three seconds");
});
dispatch_group_wait dispatch_group_notify
/// 添加3个Block到Global Dispatch Quene,全部执行完毕后执行Main Dispatch Quene的Block。
dispatch_queue_t quene = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, quene, ^{
NSLog(@"blk0");
});
dispatch_group_async(group, quene, ^{
NSLog(@"blk1");
});
dispatch_group_async(group, quene, ^{
NSLog(@"blk2");
});
/// 一旦调用了dispatch_group_wait函数,该函数就处于调用的状态不返回。即执行dispatch_group_wait函数的现在的线程(当前线程)停止。dispatch_group_wait的第二个参数指定DISPATCH_TIME_NOW,则不用任何等待即可判定属于Dispatch Group的处理是否执行结束。
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
dispatch_group_wait(group, DISPATCH_TIME_NOW);
dispatch_time_t timeWait = dispatch_time(DISPATCH_TIME_NOW, 1ull*NSEC_PER_SEC);
long result = dispatch_group_wait(group, timeWait);
if (result == 0) {
/// 属于 Dispatch Group的全部 处理执行结束
}
else{
/// 属于Dispatch Group的某一个处理还在执行中。
}
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"done");
});
dispatch_barrier_async
dispatch_barrier_async函数会等待追加到Concurrent Dispatch Quene上的并行执行的处理全部结束以后,再讲指定的处理追加到该Concurrent Dispatch Quene中。然后再由dispatch_barrier_async函数追加的处理执行完毕后,Concurrent Dispatch Quene才恢复为一般的动作,追加到该Concurrent Dispatch Quene的处理又开始并行执行。
使用Concurrent Dispatch Quene和dispatch_barrier_async函数可以实现高效率的数据库访问和文件访问。
dispatch_queue_t quene = dispatch_queue_create("com.example.gcd.ForBarrier", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(quene, blk0_for_reading);
dispatch_async(quene, blk1_for_reading);
dispatch_async(quene, blk2_for_reading);
dispatch_async(quene, blk3_for_reading);
dispatch_barrier_async(quene, blk_for_writing);
dispatch_async(quene, blk4_for_reading);
dispatch_async(quene, blk5_for_reading);
dispatch_async(quene, blk6_for_reading);
dispatch_sync
当前dispatch_sync()函数在主线程中调用,调用dispatch_sync()函数会立即阻塞调用时该函数所在的线程,即主线程等待dispatch_sync()函数返回。
dispatch_sync()函数追加任务(即Block代码块)到主队列dispatch_get_main_queue()中。
主队列是一种特殊的串行队列,主队列的任务在主线程中执行,但此时主线程被阻塞,无法执行Block代码块,导致dispatch_sync()函数无法返回,一直等待Block被主线程执行,最终导致死锁。
dispatch_queue_t mainQuene = dispatch_get_main_queue();
dispatch_sync(mainQuene, ^{
NSLog(@"dispatch_sync");
});
dispatch_async(mainQuene, ^{
NSLog(@"dispatch_async");
dispatch_sync(mainQuene, ^{
NSLog(@"dispatch_sync");
});
});
dispatch_queue_t MySerialDispatchQuene = dispatch_queue_create("com.example.gcd.MySerialDispatchQuene", NULL);
dispatch_async(MySerialDispatchQuene, ^{
dispatch_sync(MySerialDispatchQuene, ^{
NSLog(@"dispatch_sync");
});
});
dispatch_apply
/// dispatch_apply函数按指定的次序将指定的block追加到指定的Dispatch Quene中,并等待全部处理执行结束。
/// 因为在Global Dispatch Quene中,所以各个处理的执行时间不定,即0-9并不是按顺序输出。
dispatch_queue_t quene = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_apply(10, quene, ^(size_t index) {
NSLog(@"%zu",index);
});
NSLog(@"done");
/// 由于dispatch_apply函数与dispatch_sync函数相同,会等待处理执行结束,因此推荐在dispatch_async函数中非同步地执行dispatch_apply函数
NSArray *array = @[@"0",@"1",@"2"];
dispatch_async(quene, ^{
dispatch_apply([array count], quene, ^(size_t index) {
/// 并列处理包含在NSArray对象的全部对象
});
/// dispatch_apply 函数中的处理全部执行结束
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"done");
});
});
dispatch_suspend dispatch_resume
dispatch_queue_t quene = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
/// 挂起与恢复指定的Dispatch Quene。这些函数对已经执行的处理没有影响。挂起后,追加到Dispatch Quene中但尚未执行的处理在此之后停止执行,而恢复则使得这些处理能够继续执行。
dispatch_suspend(quene);
dispatch_resume(quene);
dispatch_semaphore_t
/// 下列代码执行后由内存错误导致应用程序异常结束的概率很高
// NSUInteger count = 10000;
// dispatch_queue_t quene = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// NSMutableArray *array = [NSMutableArray arrayWithCapacity:count];
// for (NSUInteger i=0; i<count; i++)
// {
// dispatch_async(quene, ^{
// [array addObject:[NSNumber numberWithInteger:i]];
// });
// }
dispatch_queue_t quene = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
/// Dispatch Semaphore 是持有计数的信号,该技术是多线程编程中的计数类型信号。计数为0时等待,计数为1或大于1时,减去1而不等待。
/// 初识设定值为1,保证可访问NSMutableArray类对象的线程同时只能有1个。
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
NSMutableArray *array = [[NSMutableArray alloc] init];
for (int i=0; i<10000; i++)
{
dispatch_async(quene, ^{
/// dispatch_semaphore_wait函数等待Dispatch Semaphore的技术值大于或者等于1。当计数值大于或者等于1时,或者在待机中计数值大于或者等于1时,对该计数进行减法并从dispatch_semaphore_wait函数返回。dispatch_semaphore_wait函数返回0时,可安全的执行需要进行排他控制的处理。
/// 一直等待,直到Dispatch Semaphore的计数值达到大于等于1
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
[array addObject:[NSNumber numberWithInt:i]];
/// 排他控制结束,通过dispatch_semaphore_signal函数将Dispatch Semaphore的计数值加1.如果有通过dispatch_semaphore_wait函数等待Dispatch Semaphore的计数值增加的线程,就由最先等待的线程执行
dispatch_semaphore_signal(semaphore);
});
}
NSLog(@"%@",array);
dispatch_once_t
/// 该源代码即使在多线程环境下执行,也可保证百分之百安全
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
/// 初始化
});
dispatch_io
在读取较大文件时,如果将文件分成合适的大小并使用Global Dispatch Quene并列读取的话,会提高读取速度。能实现这一功能的就是Dispatch I/O 和 Dispatch Data
dispatch_source_t
Dispatch Source是BSD系内核惯有功能kquene的包装。kquene是在XNU内核中发生各种事件时,在应用程序编程方执行处理的技术。其CPU负荷非常小,尽量不占用资源。kquene可以说是应用程序处理XNU内核中发生的各种事件的方法中最优秀的一种。
Dispatch Source可处理以下事件:
- DISPATCH_SOURCE_TYPE_DATA_ADD 变量增加
- DISPATCH_SOURCE_TYPE_DATA_OR 变量OR
- DISPATCH_SOURCE_TYPE_DATA_MACH_SEND MACH端口发送
- DISPATCH_SOURCE_TYPE_DATA_MACH_RECV MACH端口接收
- DISPATCH_SOURCE_TYPE_DATA_PROC 检测到与进程相关的事件
- DISPATCH_SOURCE_TYPE_DATA_READ 可读取文件映像
- DISPATCH_SOURCE_TYPE_DATA_SINGLE 接收信号
- DISPATCH_SOURCE_TYPE_DATA_TIMER 定时器
- DISPATCH_SOURCE_TYPE_DATA_VNODE 文件系统有变更
- DISPATCH_SOURCE_TYPE_DATA_WRITE 可写入文件映像
/// 指定DISPATCH_SOURCE_TYPE_TIMER作为Dispatch Source
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue());
/// 将定时器设定为15s后,不指定为重复,允许延迟1s
dispatch_source_set_timer(timer, dispatch_time(DISPATCH_TIME_NOW, 15ull*NSEC_PER_SEC), DISPATCH_TIME_FOREVER, 1ull*NSEC_PER_SEC);
dispatch_source_set_event_handler(timer, ^{
NSLog(@"wakeup!");
dispatch_source_cancel(timer);
});
dispatch_source_set_cancel_handler(timer, ^{
NSLog(@"cancled");
});
/// 启动Dispatch Source
dispatch_resume(timer);
网友评论