GCD的API:
介绍完GCD,接下来让我们进一步看下GCD的API;
dispatch_set_target_queue:
使用dispatch_queue_create生成的Dispatch Queue使用的都是与Global Dispath Queue默认优先级相同的执行优先级线程;
变更生成的Dispatch Queue的执行优先级就要使用dispatch_set_target_queue函数;
注意,第一个参数不可以指定为系统提供的Dispatch Queue;
【Code-dispatch_set_target_queueTmp】
/*dispatch_set_target_queue*/
-(void)dispatch_set_target_queueTmp{
//需要变更优先级的Dispatch Queue指定为第一个参数
dispatch_queue_t serialQueue = dispatch_queue_create("com.example.test1", NULL);
//指定与要使用的执行优先级相同优先级的Dispatch Queue为第二个参数
dispatch_queue_t globalQueueBackgroundPriority = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
//将serialQueue的默认执行优先级变更为与globalQueueBackgroundPriority一样的后台执行优先级
dispatch_set_target_queue(serialQueue, globalQueueBackgroundPriority);
}
虽然,表面上我们只是变更了Dispatch Queue的执行优先级,其实还改变了Dispatch Queue的执行阶层;
为什么这么说呢,其实还是优先级起的作用,比如相同优先级的两个Dispatch Queue,在同一时刻只能执行一个Dispatch Queue的处理;
我们之前知道,多个Serial Dispatch Queue是并行处理的,如果将他们使用dispatch_set_target_queue函数指定为某一Serial Dispatch Queue,这样就可以防止他们并行执行了;
dispatch_after:
想要在指定时间(如3s)后执行处理的情况,可以使用dispatch_after函数实现;
【Code-dispatch_afterTmp】
/*dispatch_after*/
-(void)dispatch_afterTmp{
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 3ull*NSEC_PER_SEC);
dispatch_after(time, dispatch_get_main_queue(), ^{
NSLog(@"等待了3s后 执行!");
});
}
注意此函数,是在指定时间(从现在起3秒的时刻)追加处理到Dispatch Queue;这与3s后再追加处理处理是一样的;
这与3s后执行是不一样的:
这个例子是在主线程RunLoop中执行的,假如每隔1/60秒执行RunLoop,追加处理的Block最快需3s,最慢需要3+1/60后执行;
而且Main Dispatch Queue中还有大量的追加处理以及主线程本身的延迟等;
但是,在大致的时间延至时,使用这个还是很有效率的;
dispatch_time_t类型:
这个类型的值使用dispatch_time函数或dispatch_walltime函数作成;
dispatch_time函数:
相对时间计算,dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 3ull*NSEC_PER_SEC);
这个函数可以获取从第一个参数dispatch_time_t类型值指定的实践开始,到第二个参数指定的毫微妙单位后的时间;
数值与NSEC_PER_SEC相乘得到毫微妙的数值;ull是C语言的数值字面量,显示指明类型(ull-unsigned long long);
如果使用3ull*NSEC_PER_MSEC,表示的就是3毫秒了;
dispatch_walltime函数:
绝对时间计算,适合在dispatch_after函数中指定xx年xx月xx日xx时xx分xx秒这一绝对时间处理任务;
【Code-dispatch_walltimeTmp】
/*dispatch_walltime*/
dispatch_time_t dispatch_walltimeTmp(NSDate * date){
NSTimeInterval interval;
double second, subsecond;
struct timespec time;
dispatch_time_t milestone;
//
interval = [date timeIntervalSince1970];
subsecond = modf(interval, &second);
time.tv_sec = second;
time.tv_nsec = subsecond * NSEC_PER_SEC;
milestone = dispatch_walltime(&time, 0);
return milestone;
}
-(void)dispatch_walltimeTmp{
NSTimeInterval currentTime= [[NSDate date] timeIntervalSince1970];
NSDate * date = [NSDate dateWithTimeIntervalSince1970:(currentTime + 3600)];//3600s 即1分钟后的具体时间戳
dispatch_time_t time = dispatch_walltimeTmp(date);
dispatch_after(time, dispatch_get_main_queue(), ^{
NSLog(@"等待了1s后 执行!");
});
}
Dispatch Group:
在追加到Dispatch Queue中的多个处理都完成之后,想要执行结束处理的需求场景;如果只是Serial Dispatch Queue直接将结束处理追加即可;
但是对于Concurrent Dispatch Queue或同时使用多个Dispatch Queue时,就需要使用Dispatch Group了;
无论向Dispatch Queue中追加什么样的处理,使用Dispatch Group都可以监视这些处理执行的结束;
一旦结束,就可以将结束的处理追加到指定的Dispatch Queue中;
【Code-dispatch_groupTmp】
/*dispatch_groupTmp*/
-(void)dispatch_groupTmp{
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(@"bkl-1");
});
dispatch_group_async(group, queue, ^{
NSLog(@"bkl-2");
});
dispatch_group_async(group, queue, ^{
NSLog(@"bkl-3");
});
//通过dispatch_group_enter和dispatch_group_leave
//——并不能保证通过dispatch_group_async追加处理的执行顺序(5...4...6)
dispatch_group_enter(group);//我要通过group追加处理了
dispatch_group_async(group, queue, ^{
NSLog(@"bkl-4");
dispatch_group_leave(group);//当前的处理结束了 我要通过group移除已完成的这个处理了
});
//
dispatch_group_enter(group);
dispatch_group_async(group, queue, ^{
NSLog(@"bkl-5");
dispatch_group_leave(group);
});
//
dispatch_group_enter(group);
dispatch_group_async(group, queue, ^{
NSLog(@"bkl-6");
dispatch_group_leave(group);
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"Done");
});
}
dispatch_group_async与dispatch_asyng一样,都是追加Block到指定的Dispatch Queue中;
dispatch_group_notify函数会将执行的Block追加到Dispatch Queue中,它的第一个参数指定要监视的Dispatch Group;
dispatch_group_wait:
Dispatch Group中也可以使用dispatch_group_wait函数等待全部处理执行结束;
【Code-dispatch_group_waitTmp1】
/*dispatch_group_wait*/
-(void)dispatch_group_waitTmp1{
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(@"bkl-1");
});
dispatch_group_async(group, queue, ^{
NSLog(@"bkl-2");
});
dispatch_group_async(group, queue, ^{
NSLog(@"bkl-3");
});
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);//一直等待 知道所有的处理结束 中途不能取消
NSLog(@"Done");
}
【Code-dispatch_group_waitTmp2】
-(void)dispatch_group_waitTmp2{
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(@"bkl-1");
});
dispatch_group_async(group, queue, ^{
NSLog(@"bkl-2");
});
dispatch_group_async(group, queue, ^{
NSLog(@"bkl-3");
});
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 1ull*NSEC_PER_SEC);
long result = dispatch_group_wait(group, time);//等待1s
if (result == 0) {
NSLog(@"Done");
}else{
NSLog(@"Doing");
}
}
一旦调用dispatch_group_wait函数,当前的线程就会等待,直到指定时间结束或所有处理都已结束,线程结束;
在第二个示例中,如果直接指定DISPATCH_TIME_NOW,则不用任何等待即可判定属于Dispatch Group的处理是否都已结束;
dispatch_barrier_async:
像栅栏一样的函数:
我们知道访问数据库或文件时,使用Serial Dispatch Queue可以避免数据竞争问题;
为了高效率的访问,我们可以将读的处理追加到Concurrent Dispatch Queue,而写的处理依旧使用Serial Dispatch Queue;
相应的场景虽然可以使用使用之前介绍的API实现,但是GCD也提供了更加聪明的解决方案:dispatch_barrier_async函数;
【Code-dispatch_barrier_asyncTmp】
/*dispatch_barrier_async*/
-(void)dispatch_barrier_asyncTmp{
void (^blk1Read)(void) = ^{NSLog(@"blk1Read");};
void (^blk2Read)(void) = ^{NSLog(@"blk2Read");};
void (^blk3Read)(void) = ^{NSLog(@"blk3Read");};
void (^blk4Write)(void) = ^{NSLog(@"blk4Write");};
void (^blk5Read)(void) = ^{NSLog(@"blk5Read");};
void (^blk6Read)(void) = ^{NSLog(@"blk6Read");};
void (^blk7Read)(void) = ^{NSLog(@"blk7Read");};
dispatch_queue_t queue = dispatch_queue_create("com.example.concurrent", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, blk1Read);
dispatch_async(queue, blk2Read);
dispatch_async(queue, blk3Read);
// dispatch_async(queue, blk4Write);
dispatch_barrier_async(queue, blk4Write);
dispatch_async(queue, blk5Read);
dispatch_async(queue, blk6Read);
dispatch_async(queue, blk7Read);
}
该函数与dispatch_queue_create生成的Concurrent Dispatch Queue一起使用;
在上述示例中,我们想使写入的数据,在bkl5-7read的处理中生效,如果不使用此函数,会出现许多问题,存在多个写入时更是如此;
dispatch_barrier_async函数的作用:
dispatch_barrier_async函数会等待追加到Concurrent Dispatch Queue上的并行执行处理全部结束;
再将指定的处理追加到该Concurrent Dispatch Queue中;
然后由dispatch_barrier_async函数追加的处理执行完毕后,才将Concurrent Dispatch Queue恢复为一般动作;
这样追加到Concurrent Dispatch Queue的后续处理又开始并行执行了;
bkl-1-read |
bkl-4-write
| bkl-5-read |
bkl-2-read | bkl-6-read | |
bkl-3-read | bkl-7-read |
仅使用dispatch_barrier_async代替dispatch_async即可,且使用dispatch_barrier_async和Concurrent Dispatch Queue可以高效的实现数据库访问和文件访问;
dispatch_sync:
dispatch_async表示“非同步”,不等待处理执行结束;
dispatch_sync则表示“同步”,在追加的Block结束之前,会一直等待;
和dispatch_group_wait类似,这里的“等待”,意味着当前线程停止;
死锁问题:
dispatch_sync使用简单,也容易引入死锁问题,使用时一定要慎重:最好在所有的处理结束之后,同步处理结果时再使用;
【Code-deadlockTmp1】
/*deadlock*/
-(void)deadlockTmp1{
dispatch_queue_t queue = dispatch_get_main_queue();//serial dispatch queue均会发生死锁
//没办法等待当前正在执行的处理结束,因为当前的这个处理就是运行这段代码;
dispatch_sync(queue, ^{
NSLog(@"What?");
});
}
【Code-deadlockTmp2】
-(void)deadlockTmp2{
dispatch_queue_t queue = dispatch_get_main_queue();//serial dispatch queue均会发生死锁
dispatch_async(queue, ^{
//内侧的Block等待它所在的Block执行处理结束,也会发生死锁;
dispatch_sync(queue, ^{
NSLog(@"What?");
});
});
}
dispatch_apply:
该函数按指定的次数将指定的Block追加到指定的Dispatch Queue中,并等待全部的处理结束;
这个和dispatch_sync类似(dispatch_apply只是等待本身Block的处理结束);
由于会等待处理执行结束,因此推荐非同步的执行此函数;
【Code-dispatch_applyTmp】
/*dispatch_apply*/
-(void)dispatch_applyTmp{
NSArray * array = @[@"1",@"2",@"3",@"4",@"5",@"6",@"7",@"8",@"9",@"10"];
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//在Concurrent Dispatch Queue中非同步执行
dispatch_async(queue, ^{
//等待全部处理结束
dispatch_apply(10, queue, ^(size_t index) {
//并列处理array全部对象
NSLog(@"%zu-%@",index,array[index]);
});
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"done");
});
});
}
dispatch_suspend/dispatch_resume:
当追加大量处理时,如果需要在中间某个Block捕获到需要的结果时,暂时不继续接下来的处理,就可以使用dispatch_suspend函数;
当需要执行的时候,可以使用dispatch_resume恢复后续的处理;
操作的是具体的queue:
dispatch_suspend(queue);
dispatch_resume(queue);
这下函数对已经执行的处理没有影响;
挂起后,追加到Dispatch Queue中但尚未执行的处理在此之后停止执行;
而恢复则使得这些处理能够继续执行;
Dispatch Semophore:
当我们使用如下方式,不考虑顺序将数据追加到MSMutableArray中时,执行后由内存错误导致应用程序异常结束的概率较高;
这个时候就需要Dispatch Semaphore;
【Code-dispatch_semophore_Problem】
/*dispatch semaphore*/
-(void)dispatch_semophore_Problem{
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
NSMutableArray * array = @[].mutableCopy;
//出现 由内存错误导致的问题概率较高
for (int i = 0; i<100; i++) {
dispatch_async(queue, ^{
[array addObject:[NSNumber numberWithInt:i]];
});
}
}
Dispatch Semophore是持有计数的信号:
该计数是多线程编程中的计数类型信号;计数=0等待,计数>0时,可减1,不等待;
【Code-dispatch_semophoreTmp1】
-(void)dispatch_semophoreTmp1{
//生成一个Semaphore 将计数值初始化为1(保证执行处理的线程同时只有一个)
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
//dispatch_semaphore_wait等待当前semaphore信号值大于等于1,其第二个参数指定等待时间
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
}
【Code-dispatch_semophoreTmp2】
-(void)dispatch_semophoreTmp2{
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 1ull * NSEC_PER_SEC);
//生成一个Semaphore 将计数值初始化为1(保证执行处理的线程同时只有一个)
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
//dispatch_semaphore_wait等待当前semaphore信号值大于等于1,其第二个参数指定等待时间
long result = dispatch_semaphore_wait(semaphore, time);
if (result == 0) {
//等待到了指定的时间 且 计数值>=1,所以计数值减1,并执行需要的排他处理
//该处理结束时需要使用dispatch_semaphore_signal函数将Dispatch Semaphore计数值加1
dispatch_semaphore_signal(semaphore);
}else{
//计数值=0,达到指定时间后 待机
}
}
现在让我们使用Dispatch Semophore解决下dispatch_semophore_Problem中可能出现的问题:
在没有Serial Dispatch Queue和dispatch_barrier_async函数那么大粒度 且 一部分处理需要进行排他控制时,Dispatch Semophore很有用;
【Code-dispatch_semophoreTmp3】
-(void)dispatch_semophoreTmp3{
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//Dispatch Semaphore计数初始值设为1,保证可访问NSMutableArray类的线程同时只能有一个
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
NSMutableArray * array = @[].mutableCopy;
//出现 由内存错误导致的问题概率较高
for (int i = 0; i<100; i++) {
dispatch_async(queue, ^{
//等待信号量的计数值>=1
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
//等到了计数值=1,代码执行到此 将计数值-1,此时为0;使用一个线程安全的进行array更新;
[array addObject:[NSNumber numberWithInt:i]];
//排他控制处理结束,通过dispatch_semaphore_signal函数,将计数值+1
dispatch_semaphore_signal(semaphore);
});
}
}
dispatch_once:
该函数保证程序只执行一次指定处理;
在生成单例对象时使用;
【Code-dispatch_once_problem】
/*dispatch_once*/
-(void)dispatch_once_problem{
static int initialized = NO;
if (initialized == NO) {
//初始化(多数i情况下还是安全的 但在多核CPU中 可能会出现初始化多次的情形)
initialized = YES;
}
}
【Code-dispatch_onceTmp】
-(void)dispatch_onceTmp{
static dispatch_once_t pred;
dispatch_once(&pred, ^{
//初始化(优点:多线程下执行 依然安全)
});
}
Dispatch I/O:
适用于读取较大文件时,一次使用多个线程并列读取,效率更高(写与读类似);
实现此功能的就是Dispatch I/O和Dispatch Data;
Dispatch Data:
分割和读取数据通过使用Dispatch Data可更为简单地进行结合和分割;
注:主要函数dispatch_io_create()等,在为了提高文件读取速度时可以使用,没有实现示例,需要的同学自行查阅。
Dispatch Source:
这个Dispatch Source有许多种类的事件,当对应的事件发生时,可以在指定的Dispatch Queue中执行事件的处理;
如果说Dispatch Queue是我们注定追加处理,那么Dispatch Source则是设定了具体的事件场景来在指定的Dispatch Queue中处理事件发生时的任务;
【Code-dispatch_sourceTmp】
/*Dispatch Source*/
-(void)dispatch_sourceTmp{
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue());
//timer定时器 的起始时间为15s之后
//timer定时器 的执行间隔为DISPATCH_TIME_FOREVER 即不重复
//timer定时器 的允许的时间延迟是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(@"do something");
dispatch_source_cancel(timer);
});
//指定取消Dispatch Sourced时的处理
dispatch_source_set_cancel_handler(timer, ^{
NSLog(@"do cancel");
});
//启动 该定时器(Dispatch Sourced)
dispatch_resume(timer);
}